Roslyn
Roslyn 是微软公司开源的 .NET 编译器。
编译器支持 C# 和 Visual Basic 代码编译,并提供丰富的代码分析 API。
Roslyn不仅仅可以直接编译输出,而且开放了编译的API,使得代码脚本化成为了可能。
关于Roslyn,不做过多介绍,想要了解的各位请移步官方说明地址:github.com/dotnet/rosl…
本文主要介绍的是如何加载C#脚本,并在C#程序中调用并执行脚本
第一个C#脚本
话不多说直接进入正题,实现一个基本的控制台打印脚本。
第一步,安装Microsoft.CodeAnalysis.CSharp.Scripting包。
第二步,在文本Script.txt中准备一段C#脚本
using System;
public void Script1()
{
Console.WriteLine("Hello C# Script");
}
第三步,创建脚本并执行脚本
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
var code = File.ReadAllText(@"C:\Users\Palink\Desktop\script.txt");
var script1 = CSharpScript.RunAsync(code, ScriptOptions.Default).Result;
Task.Run(() => script1.ContinueWithAsync("Script1()"));
Console.ReadKey();
控制台输出结果如下:
引用其他类库
一般的如果我们需要使用其他程序集的类我们需要在脚本配置选项ScriptOptions里添加该程序集的引用。如我们需要使用System.IO.File我么可以这么做。
var scriptOptions = ScriptOptions.Default
.AddReferences(typeof(System.IO.File).Assembly);
注意:这里使用了AddReferences方法,使用WithReferences会覆盖25个默认的程序集引用。
接下来就只需要在脚本Run的时候传入该配置即可
var script1 = CSharpScript.RunAsync(code, scriptOptions)
.Result;
我们来回顾下第一个脚本,在调用System名称空间下的Console.WriteLine("Hello C# Script")时似乎没有像使用System.IO.File一样添加引用。这是因为上面的脚本使用了默认的脚本配置选项ScriptOptions.Default,配置选项默认为我们添加了C#常用25个程序集,其中就包含了System.Console。详细的元数据引用可以查看ScriptOptions.Default的MetadataReferences属性。
好了,我们开始脚本改造工作
using System;
using System.IO;
public void Script1()
{
var txt = File.ReadAllText(@"C:\Users\Palink\Desktop\script.txt");
Console.WriteLine(txt);
}
由于引入了File所以我们需要添加File的程序集,改造下C#代码,结果如下
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
var scriptOptions = ScriptOptions.Default
.AddReferences(typeof(File).Assembly);
var code = File.ReadAllText(@"C:\Users\Palink\Desktop\script.txt");
var script1 = CSharpScript.RunAsync(code, scriptOptions)
.Result;
Task.Run(() =>
script1.ContinueWithAsync("Script1()"));
Console.ReadKey();
执行结果如下:
脚本字段
将上面脚本的文件路径用字段path代替
using System;
using System.IO;
string path;
public void Script1()
{
var txt = File.ReadAllText(path);
Console.WriteLine(txt);
}
在c#中对path字段赋值
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
var scriptOptions = ScriptOptions.Default
.AddReferences(typeof(File).Assembly);
var code = File.ReadAllText(@"C:\Users\Palink\Desktop\script.txt");
var script1 = CSharpScript.RunAsync(code, scriptOptions)
.Result;
var path = script1.Variables.ItemRef(0);
path.Value = @"C:\Users\Palink\Desktop\script.txt";
script1.Variables.SetItem(0, path);
Task.Run(() =>
script1.ContinueWithAsync("Script1()"));
Console.ReadKey();
执行结果同上。
注意:脚本中不能包含命名空间的定义
优势:通过加载C#脚本的方式去执行脚本代码避免程序重新编译的麻烦。
弊端:加载脚本时间较长、安装包很重很重(超过5M的大小)