关键词:
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 Proxy0 : 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
[006]了解roslyn编译器(代码片段)
...;在执行模型的不同阶段有两个不同的编译器:一个叫Roslyn编译器,负责把C#和VB代 查看详情
[.net大牛之路006]了解roslyn编译器(代码片段)
...中,在执行模型的不同阶段有两个不同的编译器:一个叫Roslyn编译器,负责把C#和VB代码编译为程序集; 查看详情
roslyn如何使用msbuildcopy复制文件(代码片段)
本文告诉大家如何在MSBuild里使用Copy复制文件需要知道Rosyln是MSBuild的dotnetcore版本。在MSBuild里可以使用很多命令,本文告诉大家如何使用Copy这个Task来复制文件在开始本文之前,希望大家已经知道了一些关于csproj文件格式&... 查看详情
roslyn代码分析从无错误的解决方案返回错误的构建错误(代码片段)
我尝试使用Roslyn分析一个非常简单的C#解决方案,一个具有简单框架程序的控制台应用程序:usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceCA5classProgramstaticvoidMain(string[]args)上面的代码在... 查看详情
roslyn打包nuget包buildtransitive文件夹用于穿透依赖传递拷贝文件(代码片段)
默认的PackageReference可以实现传递依赖,传递依赖的含义是是假定B项目安装了A库,而C项目依赖B项目,那么C项目将会自然拿到A库的DLL引用。但默认的NuGet包的构建指导文件targets命令是不会在传递执行的,也就是如... 查看详情
从 Roslyn 代码分析器调用分析程序集的方法
】从Roslyn代码分析器调用分析程序集的方法【英文标题】:InvokemethodofanalyzedassemblyfromaRoslynCodeAnalyzer【发布时间】:2021-10-2823:11:49【问题描述】:我有一个C#roslyn代码分析器,它需要分析给定类的泛型方法调用的使用场景。我正... 查看详情
如何在 Roslyn 代码生成器中生成数组类型?
】如何在Roslyn代码生成器中生成数组类型?【英文标题】:HowdoIgenerateanarraytypeinaRoslyncodegenerator?【发布时间】:2018-07-0322:27:52【问题描述】:我想生成一个返回类型为Foo[]的方法。我的代码大致是这样的(usingstaticSyntaxFactory):v... 查看详情
使用 Roslyn 代码分析在引用的程序集中查找符号
】使用Roslyn代码分析在引用的程序集中查找符号【英文标题】:FindingasymbolinareferencedassemblyusingRoslynCodeAnalysis【发布时间】:2021-12-1606:40:34【问题描述】:我需要在所分析的编译的引用程序集中搜索具有指定类名的类型,无论它... 查看详情
Roslyn:如何修复 RS2008 警告?
】Roslyn:如何修复RS2008警告?【英文标题】:Roslyn:HowtofixRS2008warning?【发布时间】:2021-12-0720:22:34【问题描述】:这里也有人问这个问题:https://github.com/dotnet/roslyn/issues/57292在Roslyn3.8.0中,以下代码出现RS2008警告(为包含规则“EO... 查看详情
如何使用 roslyn 在 C# 中生成/编辑打字稿代码
】如何使用roslyn在C#中生成/编辑打字稿代码【英文标题】:Howtogenerate/edittypescriptcodeinc#usingroslyn【发布时间】:2021-09-2506:46:49【问题描述】:我有一个打字稿文件,我想从代码中动态编辑它。我想将打字稿文件的路径传递给某种A... 查看详情
使用 Roslyn 编译 C# 项目时,如何避免完全重新编译?
】使用Roslyn编译C#项目时,如何避免完全重新编译?【英文标题】:HowcanIavoidafullrecompilewhencompilingaC#projectwithRoslyn?【发布时间】:2014-11-0104:05:07【问题描述】:我在我的一个C#项目中使用Roslyn生成一些代码。目前Roslyn会拿下整个... 查看详情
属性挂钩 MSBuild/Roslyn
】属性挂钩MSBuild/Roslyn【英文标题】:AttributehookingMSBuild/Roslyn【发布时间】:2021-08-2401:41:23【问题描述】:如果我想创建一个属性(从System.Attribute派生),它可以连接到.NET构建过程并转换/转换标准C#自动属性,例如:[Notify]public... 查看详情
[007]详解.net程序集(代码片段)
上一篇我们介绍了Roslyn编译器,我们知道,我们编写的C#/VB代码经过Roslyn编译器编译后会生成程序集文件。按照之前讲的.NET执行模型的顺序,这一篇我具体讲讲程序集。1什么是程序集我们编写的C#代码经过编译会生成... 查看详情
Roslyn - 如何用多个节点替换多个节点?
】Roslyn-如何用多个节点替换多个节点?【英文标题】:Roslyn-HowcanIreplacemultiplenodeswithmultiplenodeseach?【发布时间】:2017-04-2301:54:11【问题描述】:背景:将Roslyn与C#结合使用,我正在尝试扩展自动实现的属性,以便访问器主体可以... 查看详情
如何删除/释放 .dll 程序集,由 Roslyn 发出
】如何删除/释放.dll程序集,由Roslyn发出【英文标题】:Howtodelete/free.dllassembly,emittedwithRoslyn【发布时间】:2018-07-2614:30:06【问题描述】:我正在尝试创建一个小型Web应用程序,它将接受带有代码和变量的POST请求,在ASP.NETCore服务... 查看详情
Roslyn 的 SyntaxReceiver - 获取类实现接口
】Roslyn的SyntaxReceiver-获取类实现接口【英文标题】:Roslyn\'sSyntaxReceiver-GetClassesImplementingInterface【发布时间】:2021-09-0207:07:08【问题描述】:我正在尝试开发一个源代码生成器,以使用该接口在部分类上自动实现一个接口。我相... 查看详情
C# Roslyn 替换方法
】C#Roslyn替换方法【英文标题】:C#Roslynreplacemethods【发布时间】:2021-11-0819:44:00【问题描述】:我想重构(添加前缀)本地声明的方法及其在.cs文件中的用法实现这一目标的最佳做法是什么?我当前的代码只处理声明usingSystem;usi... 查看详情
使用 Roslyn 生成格式良好的语法
】使用Roslyn生成格式良好的语法【英文标题】:Generatingwell-formattedsyntaxwithRoslyn【发布时间】:2016-02-0117:26:52【问题描述】:我正在使用Roslyn来修改C#文件的语法。使用CSharpSyntaxRewriter,很容易在语法树中查找和替换节点。但是,... 查看详情