我正在尝试使用Roslyn执行用户在运行时定义的C#代码,类似于此示例:
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));
我的问题是脚本中使用的变量名称在编译时未知。换句话说,我不知道应该为我的globals类使用什么成员名,以及将有多少个成员(脚本参数)。
我尝试使用ExpandoObject解决此问题,但无法使其正常工作。 ExpandoObject应该在这种情况下工作吗?还有其他解决问题的方法吗?
更新
对于我的用例,最好的解决方案可能是使用System.Linq.Dynamic
:
//Expression typed in by the user at runtime
string Exp = @"A + B > C";
//The number of variables and their names are given elsewhere,
//so the parameter-array doesn't need to be hardcoded like in this example.
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[]
{
Expression.Parameter(typeof(double), "A"),
Expression.Parameter(typeof(double), "B"),
Expression.Parameter(typeof(double), "C")
},
null, Exp);
var Lambda = e.Compile();
//Fake some updates...
foreach (var i in Enumerable.Range(0,10))
{
Console.WriteLine(Lambda.DynamicInvoke(i, 3, 10));
}
答案 0 :(得分:0)
如果您可以在运行时检索所有成员名称,其数量和从输入传递来的值,则可以在运行时生成执行代码并对其进行评估。作为执行代码的简单示例,您可以为所有输入值生成变量声明,然后将它们全部求和:
// here you should put retrieved member names and their values. Just for example, currently here exist a couple of args
var variablesAndValues = new Dictionary<string, object> { ["arg_1"] = 5, ["arg_2"] = 6, ["arg_3"] = 7 };
// and you should create an execution code looks like this:
// var arg_1 = value_1;
// ..
// var arg_n = value_n;
// arg_1 + .. + arg_n
StringBuilder builder = new StringBuilder();
foreach (var item in variablesAndValues)
{
builder.Append("var ").Append(item.Key).Append(" = ").Append(item.Value).AppendLine(";");
}
var variablesCount = variablesAndValues.Count;
foreach (var item in variablesAndValues.Keys)
{
builder.Append(item);
if (--variablesCount > 0)
{
builder.Append(" + ");
}
}
var scriptState = CSharpScript.RunAsync(builder.ToString()).Result;
var result = scriptState.ReturnValue;
请注意,此示例假定所有值类型都具有sum_operation
,并且它们是默认脚本选项已知的,否则在尝试执行代码时会收到编译错误。
更新。
如果您的案例对性能至关重要,则可以创建一个脚本,该脚本将汇总所有输入参数,然后在需要时重复运行此脚本。
public class Globals
{
public int[] Args;
}
...
// create script that sum all input arguments
var script = CSharpScript.Create(@"var result = 0;
foreach (var item in Args)
{
result += item;
}
return result; ", globalsType: typeof(Globals));
// run it at twice on the user values that were received before
// also you can reuse an array, but anyway
var res1 = script.RunAsync(new Globals { Args = new[] { 5, 6, 7 } }).Result.ReturnValue;
var res2 = script.RunAsync(new Globals { Args = new[] { 7, 8, 9, 10} }).Result.ReturnValue;
这种方法在脚本代码中忽略了来自用户的输入变量名称,似乎在您的情况下无关紧要。