c# - CSharpScript:动态脚本参数名称
问题描述
我正在尝试使用 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));
我的问题是脚本中使用的变量名在编译时是未知的。换句话说,我不知道我的全局类应该使用什么成员名称以及会有多少成员(脚本参数)。
我尝试使用 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));
}
解决方案
如果您可以在运行时检索从输入传递的所有成员名称、计数和值,则可以在运行时生成执行代码并对其进行评估。作为执行代码的一个简单示例,您可以为所有输入值生成变量声明,然后对所有输入值求和:
// 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;
这种方法在脚本代码中忽略了来自用户的输入变量名称,并且在您的情况下似乎无关紧要。
推荐阅读
- awk - 递归文件包括
- html - 网站 CSS 动画闪烁的问题
- reactjs - 在 React 中获得了单击按钮的 id,但随后想在 if 中使用该 id
- r - 如何在搜索表单上找到 html_node?
- kotlin - 来自 recyclerview 的 Kotlin 意图 onclick。holder.title.context 上的错误
- python - 当我附加标签 tf.function 时,Tensorflow while_loop 抛出错误
- woocommerce - WooCommerce 问题,部分订单已付款但仍处于“待处理订单”状态
- spring - 如何在 Spring Boot 中为 Quartz 表使用自定义表名
- css - CSS网格输入大小增加
- github - 在 wiki 编辑上触发 Github 操作?