使用 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 把握的輸入傳給表達式,而後表達式就能夠計算結果,這個就是咱們在工做流引擎裏面要的表達式計算了。

相關文章
相關標籤/搜索