使用 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#脚本,首先进行如下几步准备工作。

1、通过Nuget 安装Microsoft.CodeAnalysis.CSharp.Scripting

2、在代码中增加如下命名空间的引用。

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

经典的HelloWorld

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

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);
    }

从上述代码中可以看出,执行一个脚本还是比较简单的, 可以通过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,而是一个异常:

究其原因,是因为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 把握的输入传给表达式,然后表达式就可以计算结果,这个就是我们在工作流引擎里面要的表达式计算了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术之路

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细...

2878
来自专栏debugeeker的专栏

某代码查看器的保护突破

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

681
来自专栏分布式系统和大数据处理

Command模式入门

提起Command模式,我想没有什么比遥控器的例子更能说明问题了,本文将通过它来一步步实现GOF的Command模式。

1022
来自专栏林德熙的博客

C# 如何在项目引用x86 x64的非托管代码

因为现在的项目使用的是 AnyCpu 在 x86 的设备使用的是x86,在x64使用的是x64,但是对于非托管代码,必须要在x64使用x64的dll,在x86使...

3721
来自专栏陈仁松博客

UWP基础教程 - XAML开篇

XAML是英文Extensible Application Markup Language的缩写,中文可以称为“可扩展应用程序标记语言”,是基于Extensiv...

4198
来自专栏张善友的专栏

Ibatisnet Quick Start

准备工作 1. 下载ibatis软件包(http://ibatis.apache.org/dotnetdownloads.html)。 2. 创建测试数据库,并...

1968
来自专栏大内老A

让我们的ASP.NET MVC应用可以单独维护验证消息

在项目开发中,我们会使用到很多的描述性文字,比如验证消息、错误消息和确认消息等,让这些文本消息具有可维护性具有重要的意义。虽然我们可以将它们存储于资源文件中,并...

2007
来自专栏程序员的SOD蜜

C#调用C和C++函数的一点区别

最近做U800电话的二次开发,需要调用厂商的C函数库来打电话,后来想加入通话录音功能,但发现程序默认生产的WAV文件过大,又找了个WAV转MP3的C++函数库程...

2536
来自专栏大内老A

IoC+AOP的简单实现

对EnterLib有所了解的人应该知道,其中有一个名叫Policy Injection的AOP框架;而整个EnterLib完全建立在另一个叫作Unity的底层框...

2109
来自专栏debugeeker的专栏

adobe flash player升级coredump分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

631

扫码关注云+社区

领取腾讯云代金券