.net core ——利用 roslyn 编译C#代码

目录

Rosyln介绍

实时编译c#文本为dll

1、增加PreserveCompilationContext配置

2、引用包

3、利用 DependencyContext 获取编译引用dll

完整例子

引用test库

总结

注脚


 

Rosyln介绍

Rosyln1 是 .NET Core和 .NET 4.6+ 中 的C# 、VB的编译器,宇宙最强IDE Visual Studio 也是使用其来编译代码的, 基于编译器也是服务的理念,微软开发者把其独立出来,并开源维护,开源地址参见注脚2。
在工作流引擎 或是规则引擎中有时候都需要一项功能是计算表达式, 以前我们通常借助于Antlr 3 ,根据特殊的语法(文法)来构建复杂的解析器代码。它就像是一个用于语言解析的加强版的正则表达式。当然你也可以采用目前流行的解释型语言引擎来完成此事,可以参考我之前的文章,有一篇有关Javascript引擎的介绍。
Rosyln 下也有一个类似的C#编译器脚本引擎 C# Scripting API 4 ,来完成 类似下面的表达式评估。
安装包 * Microsoft.CodeAnalysis.CSharp.Scripting*

int result = await CSharpScript.EvaluateAsync<int>("1 + 2");

实时编译c#文本为dll

最新的包 Microsoft.CodeAnalysis.CSharp 已经支持 .net core 了,因此 .net core 下编译c#字符串已经没有任何问题了。按照下列步骤进行:

1、增加PreserveCompilationContext配置

编辑你的csproj项目文件,增加如下配置

<PropertyGroup>
  <PreserveCompilationContext>true</PreserveCompilationContext>
 </PropertyGroup>

2、引用包

编辑你的csproj项目文件,增加如下配置

 <ItemGroup>
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.11.0-beta1-final" />
    <PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
  </ItemGroup>

3、利用 DependencyContext 获取编译引用dll

MetadataReference[] _ref = 
    DependencyContext.Default.CompileLibraries
    .First(cl => cl.Name == "Microsoft.NETCore.App")
    .ResolveReferencePaths()
    .Select(asm => MetadataReference.CreateFromFile(asm))
    .ToArray();

完整例子

MetadataReference[] _ref =
             DependencyContext.Default.CompileLibraries
                  .First(cl => cl.Name == "Microsoft.NETCore.App")
                  .ResolveReferencePaths()
                  .Select(asm => MetadataReference.CreateFromFile(asm))
                  .ToArray();         
          string testClass = @"using System; 
              namespace test{
               public class tes
               {
                 public string unescape(string Text)
                    { 
                      return Uri.UnescapeDataString(Text);
                    } 
               }
              }";         
         
          var compilation = CSharpCompilation.Create(Guid.NewGuid().ToString() + ".dll")
              .WithOptions(new CSharpCompilationOptions(
                  Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary,
                  usings: null,
                  optimizationLevel: OptimizationLevel.Debug, // TODO
                  checkOverflow: false,                       // TODO
                  allowUnsafe: true,                          // TODO
                  platform: Platform.AnyCpu,
                  warningLevel: 4,
                  xmlReferenceResolver: null // don't support XML file references in interactive (permissions & doc comment includes)
                  ))
              .AddReferences(_ref )
              )
              .AddSyntaxTrees(CSharpSyntaxTree.ParseText(testClass));
          var eResult = compilation.Emit("test.dll");

引用test库

按照上述例子的步骤进行操作,成功生成test.dll文件,我引用并测试它是否可执行。

var t = new test.tes();
var txt = t.unescape("abcdefg");

一切OK,恭喜!你已经完美搞定.net core 下的动态编译c#。

总结

难点在于DependencyContext的引入,很多人都是卡在这一步,因为.net 目标编译时默认会选用 .net framework库,那么你的dll可以生成,但是没法引入到 .net core 项目中来!

注脚


  1. https://github.com/dotnet/roslyn/wiki ↩︎

  2. https://github.com/dotnet/roslyn ↩︎

  3. https://www.antlr.org/download.html ↩︎

  4. https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples ↩︎

上一篇:基于roslyn实现函数与函数之间的依赖关系


下一篇:Roslyn让编译时候Message内容默认输出