.net 动态编译 Roslyn

.net 动态编译 Roslyn

  • 商品编号:
    #60653640_711
    • 原价:
      免费
    • 会员价:
      免费
  • 分类:
    • roslyn
  • 数量:

购物车中已存在此商品,请在购物车中操作单击跳转购物车

  • 拥有者:刘小吉
  • 开发语言:c#
  • 开发环境:vs2017
  • 商品架构:B/S
  • 代码管理工具:GitHub
  • 编码格式:UTF-8
  • 是否开源:是
  • 开源协议:Apache Licence 2.0(Apache-2.0)
  • 博客地址:http://www.cnblogs.com/liuxiaoji/

Roslyn 是以 API 为驱动的下一代编译器,集成在最新版的 Visual Studio 上。它开放 C# 和 Visual Basic 编译器的 API,使得开发者可以借助编译器进行解析代码文件、动态为编程语言增加功能、扩展编译器、自定义编译器动作等操作。

将Roslyn编译结果保存在流中,用程序集加载方法将流加载到当前程序集中,就可以在当前的程序集中调用了。

Roslyn支持两种方式的动态编译:

源代码动态编译就是对C#或VB.Net原代码进行解析编译,源代码动态编译实现简单易于上手,但是编译效率较低,适合小量的动态编译工作和初期开发人员。

源代码动态编译示例:

复制代码
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@"
    using System;
    namespace RoslynCompileSample
    {
        public class Writer
        {
            public void Write(string message)
            {
                Console.WriteLine(message);
            }
        }
    }");
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
};

CSharpCompilation compilation = CSharpCompilation.Create(
    assemblyName,
    syntaxTrees: new[] { syntaxTree },
    references: references,
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
    EmitResult result = compilation.Emit(ms);
    ms.Seek(0, SeekOrigin.Begin);
    Assembly assembly = Assembly.Load(ms.ToArray());
}
复制代码

这样就成功编译了一个动态程序集,这个程序集引用了当前运行时的程序集,在动态程序集中可以引用当前程序集的命名空间,通过下面的反射就可以调用这个动态程序集了;

复制代码
Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });
复制代码

Roslyn提供了一系列的API来供开发人员通过调用API的方式来创建一个动态程序集,通过API创建动态程序集的方式开发难度大但是编译效率高,适合需要进行大量动态编译工作的场景,适合高级开发人员,同样以上面实现的动态程序集功能为例,下面是通过API的实现:

复制代码
SyntaxTree syntaxTree = CompilationUnit()
.WithUsings(
    SingletonList<UsingDirectiveSyntax>(
        UsingDirective(
            IdentifierName("System"))))
.WithMembers(
    SingletonList<MemberDeclarationSyntax>(
        NamespaceDeclaration(
            IdentifierName("RoslynCompileSample"))
        .WithMembers(
            SingletonList<MemberDeclarationSyntax>(
                ClassDeclaration("Writer")
                .WithModifiers(
                    TokenList(
                        Token(SyntaxKind.PublicKeyword)))
                .WithMembers(
                    SingletonList<MemberDeclarationSyntax>(
                        MethodDeclaration(
                            PredefinedType(
                                Token(SyntaxKind.VoidKeyword)),
                            Identifier("Write"))
                        .WithModifiers(
                            TokenList(
                                Token(SyntaxKind.PublicKeyword)))
                        .WithParameterList(
                            ParameterList(
                                SingletonSeparatedList<ParameterSyntax>(
                                    Parameter(
                                        Identifier("message"))
                                    .WithType(
                                        PredefinedType(
                                            Token(SyntaxKind.StringKeyword))))))
                        .WithBody(
                            Block(
                                SingletonList<StatementSyntax>(
                                    ExpressionStatement(
                                        InvocationExpression(
                                            MemberAccessExpression(
                                                SyntaxKind.SimpleMemberAccessExpression,
                                                IdentifierName("Console"),
                                                IdentifierName("WriteLine")))
                                        .WithArgumentList(
                                            ArgumentList(
                                                SingletonSeparatedList<ArgumentSyntax>(
                                                    Argument(
                                                        IdentifierName("message")))))))))))))))
.NormalizeWhitespace().SyntaxTree;
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
    MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
    MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
};

CSharpCompilation compilation = CSharpCompilation.Create(
    assemblyName,
    syntaxTrees: new[] { syntaxTree },
    references: references,
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
    EmitResult result = compilation.Emit(ms);
    ms.Seek(0, SeekOrigin.Begin);
    Assembly assembly = Assembly.Load(ms.ToArray());
}
复制代码

对比两种实现方式的代码可以发现,通过API实现的动态编译就是将原本的所有关键字、标识符、连接符、修饰符、表达式等通过API的方式进行描述。

除了关键字、连接符、修饰符等API外,Roslyn还提供了包括继承、特征、约束等相关API,通过API几乎可以实现任何源码编译能实现的所有功能。

具体列子

生成webapi的接口代理

API

复制代码
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase, IOrder
    {
        [HttpGet("{id}")]
        public Order Add(int id)
        {
            return new Order();
        }

        [HttpPut]
        public Order Addx(string a)
        {
            throw new System.NotImplementedException();
        }

        [HttpPut]
        public Order Update([FromBody] Order value)
        {
            return value;
        }
    }
复制代码

动态代理

复制代码
public class ProxyClass
    {
        static readonly IDictionary<string, Type> services = new ConcurrentDictionary<string, Type>(8, 128);

        static ProxyClass()
        {
            PortsImporter.Ports<IService>();
            IEnumerable<Type> typeServices = typeof(IService).Assembly.GetTypes().Where(type =>
            {
                var typeInfo = type.GetTypeInfo();
                return typeInfo.IsInterface && typeInfo.GetCustomAttribute<BundleAttribute>() != null;
            }).ToList();

            foreach (var typeService in typeServices)
            {
                string code = GetCode(typeService);
                var assembly = GenerateProxyTree(code);
                var type = assembly.GetExportedTypes()[0];
                var fullName = typeService.FullName;
                services.Add(fullName, type);
            }
        }

        public static T CreateProxy<T>(Type proxyType, object context)
        {
            return (T)Create(proxyType, context);
        }

        public static object Create(Type proxyType, object context)
        {
            var instance = proxyType.GetTypeInfo().GetConstructors().First().Invoke(null);
            return instance;
        }

        public static T Generate<T>()
        {
            if (services.TryGetValue(typeof(T).FullName, out var type))
            {
                return CreateProxy<T>(type, null);
            }
            throw new Exception("未找到实现");
        }

        private static string GetCode(Type typeService)
        {
            StringBuilder codes = new StringBuilder();
            codes.AppendLine("using System;");
            codes.AppendLine("using Model;");
            codes.AppendLine("using System.Linq;");
            codes.AppendFormat("using {0};", typeService.Namespace);
            codes.AppendLine();
            codes.AppendLine("namespace RoslynCompileSample");
            codes.AppendLine("{");
            codes.AppendFormat("public class Proxy{0} : {1}", typeService.Name, typeService.Name);
            codes.AppendLine();
            codes.AppendLine("{");
            var methods = typeService.GetMethods(BindingFlags.Instance | BindingFlags.Public);
            foreach (var method in methods)
            {
                codes.AppendLine();
                codes.AppendFormat("public {0} {1} (", method.ReturnType.FullName, method.Name);
                List<string> parameterList = new List<string>();
                var parameters = method.GetParameters();
                foreach (var parameter in parameters)
                {
                    parameterList.Add($"{parameter.ParameterType.FullName} {parameter.Name}");
                }

                codes.Append(string.Join(',', parameterList));
                codes.AppendFormat(")");
                codes.AppendLine();
                codes.AppendLine("{");


                #region 需要自己实现的业务代码

                /*业务*/
                if (method.CustomAttributes.Any(item => item.AttributeType == typeof(HttpGetAttribute)))
                {
                    codes.AppendLine("HttpClientUtility client = new HttpClientUtility(\"http://localhost:57649/api/values\");");
                    codes.AppendFormat("return client.Get<{0}>(new string[] {{ {1}.ToString() }});", method.ReturnType, parameters.First().Name);
                }
                else
                {
                    codes.AppendLine("return null;");
                }

                #endregion

                codes.AppendLine("}");
                codes.AppendLine();
            }

            codes.AppendLine("}");
            codes.AppendLine("}");
            return codes.ToString();
        }

        /// <summary>
        /// 万能接口
        /// </summary>
        /// <param name="code">传入你要实现的代码</param>
        /// <returns>动态生成一个程序集</returns>
        public static Assembly GenerateProxyTree(string code)
        {
            Assembly assembly = null;
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
            string assemblyName = Path.GetRandomFileName();
            var references = AppDomain.CurrentDomain.GetAssemblies().Select(x => MetadataReference.CreateFromFile(x.Location));
            CSharpCompilation compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree }, references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
            using (var ms = new MemoryStream())
            {
                EmitResult result = compilation.Emit(ms);
                if (result.Success)
                {
                    ms.Seek(0, SeekOrigin.Begin);
                    assembly = Assembly.Load(ms.ToArray());
                }
            }
            return assembly;
        }

        public static void Tets()
        {
            //var code = @"using System; namespace RoslynCompileSample { public class Writer { public void Write(string message) { Console.WriteLine(message); } } }";
            //var assembly = GenerateProxyTree(code);
            //Type type = assembly.GetType("RoslynCompileSample.Writer");
            //object obj = Activator.CreateInstance(type);
            //type.InvokeMember("Write", BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, new object[] { "打印一句话" });
        }
    }
复制代码

测试调用

            /*动态编译*/
            var order = ProxyClass.Generate<IOrder>();
            var dss = order.Add(2);

github https://github.com/842549829/Roslyn

Roslyn动态编译的应用场景

用作脚本语言

学习或参与过游戏开发的人基本上都接触过lua语言,Lua是一个非常小巧的脚本语言,而且很容易嵌入其它语言中使用,很多游戏使用lua作为自己的嵌入式脚本语言来实现剧本演进、特效调用、Mod皮肤等功能,脚本语言有着便于免编译局部更新的优点,风靡全球长盛不衰的WOW就大量使用了lua脚本语言来开发前段功能。

.Net有了Roslyn后C#、VB.net也具备了脚本语言的优点,不用预先编译就能够运行,同时又具备了预编译语言的特性,执行效率更高,著名的跨平台游戏开发引擎unity/unity3D就已经提供了C#作为脚本开发语言的支持,一年揽金百亿的王者荣耀就是基于unity3D引擎开发的。

接口的动态实现

随着远程过程调用技术的兴起简化开发过程就成了框架设计开发中需要考虑的重要因素,能像实例化实体类一样的实例化接口并将接口调用映射到远程的真实接口实现是最便捷的调用方式,业务开发人员完全不用考虑网络传输、对象序列化、异步操作等远程

Roslyn动态编译包含了大量的API,限于篇幅关系,Roslyn动态编译的API将在后面的章节进行详细讲解。也可以通过在线Roslyn API生成工具(https://roslynquoter.azurewebsites.net/)进行学习。

权利声明:本站所有商品信息、客户评价等信息是初心商城重要的数据资源,未经许可,禁止非法转载使用。 注:本站商品信息均来自初心商城,其真实性、准确性和合法性由初心商城负责。

                  初心源说明:初心商城主要为程序员提供开发基础的代码源以及成熟项目,网站中所有的商品有提供收费版本的, 也有提供免费版本的,按照大家各自不同的需求进行购买。实实在在的让程序员只用专注于自己的业务实现你的小梦想, 如果您对我们的成果表示认同并且觉得对你有所帮助我们愿意接受来自各方面的支持^_^。

                  支持:用手机扫描二维码支付

                  支付宝支持我们 微信支持我们

                  您的支持将被用于:
                  1、持续深入的上传更多更好的源代码
                  2、建立更加完善的技术社区
                  3、完善现在系统出现各种问题
                  4、购买域名和租赁服务器

                  1、交易规则

                  2、发货方式

                  1、自动:在上方保障服务中标有自动发货的商品,拍下后,将会自动收到来自卖家的商品获取(下载)链接

                  2、手动:在上方保障服务中标有手动发货的商品,拍下后,卖家会收到邮件,也可通过QQ或订单中的电话联系对方。

                  3、退款说明

                  1、描述:源码描述(含标题)与实际源码不一致的(例:描述PHP实际为ASP、描述的功能实际缺少、版本不符等)

                  2、演示:有演示站时,与实际源码小于95%一致的(但描述中有"不保证完全一样、有变化的可能性"类似显著声明的除外)

                  3、发货:手动发货源码,在卖家未发货前,已申请退款的

                  4、服务:卖家不提供安装服务或需额外收费的(但描述中有显著声明的除外)

                  5、其它:如质量方面的硬性常规问题等

                  备注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。交易中的商品,卖家无法对描述进行修改!

                  4、注意事项

                  1、客户买完之后未确认收货,将不会收到下载地址和下载码,确认收货之后才能收到下载地址和下载码。

                  2、在未拍下前,双方在QQ上所商定的内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准);

                  3、在商品同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外);

                  4、在没有"无任何正当退款依据"的前提下,写有"一旦售出,概不支持退款"等类似的声明,视为无效声明;

                  5、虽然交易产生纠纷的几率很小,但请尽量保留如聊天记录这样的重要信息,以防产生纠纷时出现问题不明确的情况。

                  5、交易声明

                  1、本站作为直卖平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益;

                  2、非平台线上交易的商品,出现任何后果均与本站无关;无论卖家以何理由要求线下交易的,请联系管理举报。

                  初心Logo

                  初心商城| 初心系列| 初心博客| 版本历史| 系统反馈

                  © 2016-2019 山西米立信息技术有限公司 保留所有权利 京ICP备16055626号
                  违法和不良信息举报电话:186-2950-9347,本网站所列数据,除特殊说明,所有数据均出自我工作室
                  本网站兼容所有主流浏览器,不支持手机自适应

                  返回顶部小火箭