Roslyn

Posted liuxiaoji

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Roslyn相关的知识,希望对你有一定的参考价值。

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的主要内容,如果未能解决你的问题,请参考以下文章

Roslyn

如何在 Roslyn 代码生成器中生成数组类型?

Roslyn代码生成,如何为给定类型创建一个类型表达式。

使用 Roslyn 代码分析在引用的程序集中查找符号

使用 Roslyn 编译器服务

Roslyn:如何修复 RS2008 警告?