我试图使用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));
}发布于 2019-11-22 19:37:45
如果可以在运行时检索所有成员名称、它们的计数以及从输入中传递的值,则可以在运行时生成执行代码并对其进行评估。作为执行代码的一个简单示例,您可以为所有输入值生成变量声明,然后对所有这些值进行求和:
// 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,并且在默认脚本选项中它们是已知的,否则在尝试执行代码时会收到编译错误。
Upd。
如果您的情况是性能严格的,您可以创建一个脚本,该脚本将对所有输入参数进行求和,然后在需要时重复运行该脚本。
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;这种方法在脚本代码中忽略了来自用户的输入变量名,而且在您的情况下似乎并不重要。
https://stackoverflow.com/questions/58990746
复制相似问题