目标
编辑一段C#脚本,使用Microsoft.CodeAnalysis.Scripting 解析脚本,脚本中可能出现外部变量,找到一种通行的办法传递外部参数。
传递参数方法
根据官方文档说明,可以外部定义一个类型,然后该类型的实例传递参数,例如官方的例子:
public class Globals
{
public int X;
public int Y;
}
var globals = new Globals { X = 1, Y = 2 };
Console.WriteLine(await CSharpScript.EvaluateAsync<int>("X+Y", globals: globals));
其中,public class globals{...}必须在单独的dll中定义再引入,不能直接在同一个工程下定义。
巧用参数
我试了,参数可以是Dictionary<string,object>,这样就可以传入一个字典,然后在字典中随便放什么key,脚本里用就行了。
这还不够好,参数类型可以是Func<string,object>,这样脚本里用的变量可以通过外部赋值传进去,连Dictionary都不用了。
脚本中变量X的书写格式为 GetValue("X"),类型是object,根据需要做类型转换。
Globals的代码如下:
using System;
namespace Arim.DataX.CShaprScript
{
public class Globals
{
public Func<string, object> GetValue { get; set; }
}
}
调用方法
代码中引入了Newtonsoft.Json.JsonConvert是为了可以使用json序列化,不是必须的。
var expression = "(int)GetValue(\"X\")+(int)GetValue(\"Y\")";
var globals = new Arim.DataX.CShaprScript.Globals()
{
GetValue = (key) => key == "X" ? 1 : 2
};
Console.WriteLine( CSharpScript.EvaluateAsync(expression,
ScriptOptions.Default.WithReferences(typeof(Newtonsoft.Json.JsonConvert).Assembly),
globals: globals,
globalsType: typeof(Arim.DataX.CShaprScript.Globals)).Result);
结果为 3。
提高性能
每次调用 CSharpScript.EvaluateAsync 大约要0.1秒,应该是成需要构造表达式树,如果多次调用,可以先获得表达式对象,然后Run,性能可以提高上万倍。
if(script == null)
{
script = CSharpScript.Create(Formula,
Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default.WithReferences(typeof(Newtonsoft.Json.JsonConvert).Assembly),
globalsType: typeof(Arim.DataX.CShaprScript.Globals));
}
return script.RunAsync(globals: new CShaprScript.Globals() {InputName = sourcePropertyName, GetValue = getPropertyValue }).Result.ReturnValue;