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