使用 Roslyn 编译器服务

.NET Core和 .NET 4.6中 的C# 6/7 中的编译器Roslyn 一个重要的特性就是"Compiler as a Service",简单的讲,就是就是将编译器开放为一种可在代码中调用的服务, 一般在工做流引擎 或是规则引擎中都须要一项功能是计算表达式, 在没有Roslyn 以前我一般借助于Antlr [Antlr(“又一个语言识别工具”的缩写)是一个最初用Java编写的库,能够根据特殊的语法(文法)来构建复杂的解析器代码。它就像是一个用于语言解析的增强版的正则表达式。你能够编写某种语言的语法规则,Antlr会为你生成代码],基于Antlr 有一个轻量级的C#编译器服务Expression Evaluator正则表达式

要在本身的代码中使用Roslyn 执行C#脚本,首先进行以下几步准备工做。shell

一、经过Nuget 安装Microsoft.CodeAnalysis.CSharp.Scripting函数

二、在代码中增长以下命名空间的引用。工具

using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting; lua

经典的HelloWorldspa

首先仍是以经典的Hello World来开始介绍如何执行脚本吧。code

static void Main(string[] args)
    {
           var options =
               ScriptOptions.Default
              .AddReferences("System.Runtime, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");对象

           var bar = new Bar() { StaffId = 5686, UnitId = 2 , Age = 15};
           Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync("System.Console.WriteLine(\"hello world\");", options);blog

    }ip

从上述代码中能够看出,执行一个脚本仍是比较简单的, 能够经过Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync() 函数执行本身的脚本了,若是咱们要获取脚本的返回值,也是很容易的。

var scriptState = CSharpScript.RunAsync<int>("3+2*5", ScriptOptions.Default);

Console.WriteLine(scriptState );

在会话中执行脚本

不少时候,咱们没法一次执行全部的脚本,而是像shell中那样输入一句执行一句的。假如咱们执行以下代码

Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync("var i = 3;");
var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync("i * 2");

获得的并非咱们想要的结果6,而是一个异常:

image

究其缘由,是由于CSharpScript.RunAsync 函数每次都是在一个单独的上下文中执行的,并不会和前面的语句产生关联。若是咱们要在CSharpScript.Create()函数建立一个脚本,经过函数ContinueWith 组成一个完整的脚本运行。正确方式以下:

var s0 = CSharpScript.Create("int x = 1;");
           var s1 = s0.ContinueWith("int y = 2;");           
           var s2 = s1.ContinueWith<int>("x + y");
           Console.WriteLine(s2.RunAsync().Result.ReturnValue);

在脚本和程序中共享数据

咱们在执行脚本时,除了获取脚本的输出外,许多时候须要设置脚本的输入,要设置输入的方式也有许多。最直接的方式拼接脚本但这么作的效率和可维护性是十分差的。另外也能够经过传统的IPC通讯机制——文件、Socket等方式,这种方式一来比较麻烦,二来对于复杂的对象来讲,还牵涉到序列化,也是很是不便。

Roslyn提供了一个更为简单有效的解决办法:在会话中传入一个宿主对象,会话中的脚本程序也能访问宿主对象的各成员变量。

namespace RoslynCosonle
{
    class Program
    {
        static void Main(string[] args)
        {
            var options =
                ScriptOptions.Default
               .AddReferences("System.Runtime, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");         

            var s0 = CSharpScript.Create("int x = 1;");
            var s1 = s0.ContinueWith("int y = 2;");           
            var s2 = s1.ContinueWith<int>("x + y");
            Console.WriteLine(s2.RunAsync().Result.ReturnValue);

          

           var bar = new Bar() { StaffId = 5686, UnitId = 2 , Age = 15};
            Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync("System.Console.WriteLine( (StaffId==5686 && UnitId==2)||( UnitId == 3|| Age >10) );", options, bar);

 
        }
    }

    public class Bar
    {
        public string Foo => "Hello World!";
       
        public int StaffId { get; set; }

        public int UnitId { get; set; }

        public int Age { get; set; }
    }

经过对象Bar 把握的输入传给表达式,而后表达式就能够计算结果,这个就是咱们在工做流引擎里面要的表达式计算了。

相关文章
相关标签/搜索