我可以eval("something()");
在 JavaScript 中动态执行代码。有没有办法让我在 C# 中做同样的事情?
我正在尝试做的一个例子是:我有一个整数变量(比如i
),并且我有多个属性名称:“Property1”、“Property2”、“Property3”等。现在,我想执行一些操作关于“Property i ”属性的值i
。
这对于 Javascript 来说非常简单。有没有办法用 C# 做到这一点?
我可以eval("something()");
在 JavaScript 中动态执行代码。有没有办法让我在 C# 中做同样的事情?
我正在尝试做的一个例子是:我有一个整数变量(比如i
),并且我有多个属性名称:“Property1”、“Property2”、“Property3”等。现在,我想执行一些操作关于“Property i ”属性的值i
。
这对于 Javascript 来说非常简单。有没有办法用 C# 做到这一点?
使用 Roslyn 脚本 API(此处有更多示例):
// add NuGet package 'Microsoft.CodeAnalysis.Scripting'
using Microsoft.CodeAnalysis.CSharp.Scripting;
await CSharpScript.EvaluateAsync("System.Math.Pow(2, 4)") // returns 16
您还可以运行任何一段代码:
var script = await CSharpScript.RunAsync(@"
class MyClass
{
public void Print() => System.Console.WriteLine(1);
}")
并参考之前运行中生成的代码:
await script.ContinueWithAsync("new MyClass().Print();");
免责声明:这个答案写于 2008 年。从那时起,情况发生了翻天覆地的变化。
查看此页面上的其他答案,尤其是详细信息Microsoft.CodeAnalysis.CSharp.Scripting
。
其余答案将与最初发布的相同,但不再准确。
不幸的是,C# 不是这样的动态语言。
但是,您可以做的是创建一个包含类和所有内容的 C# 源代码文件,并通过 C# 的 CodeDom 提供程序运行它并将其编译成程序集,然后执行它。
MSDN 上的这个论坛帖子在页面下方包含一些示例代码的答案:
从字符串创建匿名方法?
我很难说这是一个很好的解决方案,但无论如何都是可能的。
你会在那个字符串中期待什么样的代码?如果它是有效代码的一小部分,例如只是数学表达式,则可能存在其他替代方案。
编辑:嗯,这教会我先彻底阅读问题。是的,反射可以在这里给你一些帮助。
如果你用 ; 分割字符串 首先,要获取单个属性,您可以使用以下代码为类的特定属性获取 PropertyInfo 对象,然后使用该对象来操作特定对象。
String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);
并不真地。您可以使用反射来实现您想要的,但它不会像在 Javascript 中那么简单。例如,如果你想将一个对象的私有字段设置为某个东西,你可以使用这个函数:
protected static void SetField(object o, string fieldName, object value)
{
FieldInfo field = o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(o, value);
}
这是c#下的eval函数。我用它从字符串转换匿名函数(Lambda 表达式)。来源: http: //www.codeproject.com/KB/cs/evalcscode.aspx
public static object Eval(string sCSCode) {
CSharpCodeProvider c = new CSharpCodeProvider();
ICodeCompiler icc = c.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("system.dll");
cp.ReferencedAssemblies.Add("system.xml.dll");
cp.ReferencedAssemblies.Add("system.data.dll");
cp.ReferencedAssemblies.Add("system.windows.forms.dll");
cp.ReferencedAssemblies.Add("system.drawing.dll");
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
StringBuilder sb = new StringBuilder("");
sb.Append("using System;\n" );
sb.Append("using System.Xml;\n");
sb.Append("using System.Data;\n");
sb.Append("using System.Data.SqlClient;\n");
sb.Append("using System.Windows.Forms;\n");
sb.Append("using System.Drawing;\n");
sb.Append("namespace CSCodeEvaler{ \n");
sb.Append("public class CSCodeEvaler{ \n");
sb.Append("public object EvalCode(){\n");
sb.Append("return "+sCSCode+"; \n");
sb.Append("} \n");
sb.Append("} \n");
sb.Append("}\n");
CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
if( cr.Errors.Count > 0 ){
MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText,
"Error evaluating cs code", MessageBoxButtons.OK,
MessageBoxIcon.Error );
return null;
}
System.Reflection.Assembly a = cr.CompiledAssembly;
object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");
Type t = o.GetType();
MethodInfo mi = t.GetMethod("EvalCode");
object s = mi.Invoke(o, null);
return s;
}
我编写了一个开源项目Dynamic Expresso,它可以将使用 C# 语法编写的文本表达式转换为委托(或表达式树)。表达式在不使用编译或反射的情况下被解析并转换为表达式树。
您可以编写如下内容:
var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");
或者
var interpreter = new Interpreter()
.SetVariable("service", new ServiceExample());
string expression = "x > 4 ? service.SomeMethod() : service.AnotherMethod()";
Lambda parsedExpression = interpreter.Parse(expression,
new Parameter("x", typeof(int)));
parsedExpression.Invoke(5);
我的工作基于 Scott Gu 的文章http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx。
所有这些肯定会奏效。就个人而言,对于那个特定的问题,我可能会采取一些不同的方法。也许是这样的:
class MyClass {
public Point point1, point2, point3;
private Point[] points;
public MyClass() {
//...
this.points = new Point[] {point1, point2, point3};
}
public void DoSomethingWith(int i) {
Point target = this.points[i+1];
// do stuff to target
}
}
当使用这样的模式时,你必须小心你的数据是通过引用而不是值来存储的。换句话说,不要对原语执行此操作。你必须使用他们臃肿的类对应物。
我意识到这不完全是问题,但这个问题已经得到了很好的回答,我认为也许另一种方法可能会有所帮助。
您可以使用反射来获取属性并调用它。像这样的东西:
object result = theObject.GetType().GetProperty("Property" + i).GetValue(theObject, null);
也就是说,假设具有该属性的对象称为“theObject”:)
您还可以实现一个 Web 浏览器,然后加载一个包含 javascript 的 html 文件。
然后你去document.InvokeScript
这个浏览器上的方法。eval 函数的返回值可以被捕获并转换成你需要的一切。
我在几个项目中做到了这一点,并且效果很好。
希望能帮助到你
使用反射在运行时针对对象解析和评估数据绑定表达式。
您可以使用原型函数来做到这一点:
void something(int i, string P1) {
something(i, P1, String.Empty);
}
void something(int i, string P1, string P2) {
something(i, P1, P2, String.Empty);
}
void something(int i, string P1, string P2, string P3) {
something(i, P1, P2, P3, String.Empty);
}
等等...
我编写了一个包SharpByte.Dynamic来简化动态编译和执行代码的任务。可以使用扩展方法在任何上下文对象上调用代码,详见此处。
例如,
someObject.Evaluate<int>("6 / {{{0}}}", 3))
返回 3;
someObject.Evaluate("this.ToString()"))
返回上下文对象的字符串表示;
someObject.Execute(@
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");
将这些语句作为脚本等运行。
可以使用工厂方法轻松获取可执行文件,如示例中所示 -您只需要源代码和任何预期命名参数的列表(使用三括号表示法嵌入标记,例如 {{{0}} },以避免与 string.Format() 以及类似 Handlebars 的语法发生冲突):
IExecutable executable = ExecutableFactory.Default.GetExecutable(executableType, sourceCode, parameterNames, addedNamespaces);
每个可执行对象(脚本或表达式)都是线程安全的,可以存储和重用,支持从脚本中记录日志,存储时间信息和遇到的最后一个异常等。还有一个 Copy() 方法在每个上编译以允许创建廉价副本,即使用从脚本或表达式编译的可执行对象作为创建其他对象的模板。
执行已编译的脚本或语句的开销相对较低,在适中的硬件上不到一微秒,并且已编译的脚本和表达式被缓存以供重用。
我试图通过名称获取结构(类)成员的值。该结构不是动态的。在我最终得到它之前,所有答案都没有奏效:
public static object GetPropertyValue(object instance, string memberName)
{
return instance.GetType().GetField(memberName).GetValue(instance);
}
此方法将按成员的名称返回成员的值。它适用于常规结构(类)。
您可以查看Heleonix.Reflection库。它提供了动态获取/设置/调用成员的方法,包括嵌套成员,或者如果明确定义了成员,您可以创建比反射更快的 getter/setter(lambda 编译成委托):
var success = Reflector.Set(instance, null, $"Property{i}", value);
或者,如果属性的数量不是无穷无尽的,您可以生成 setter 并对其进行 chache(setter 更快,因为它们是编译的委托):
var setter = Reflector.CreateSetter<object, object>($"Property{i}", typeof(type which contains "Property"+i));
setter(instance, value);
Setter 可以是类型Action<object, object>
,但实例在运行时可以不同,因此您可以创建 setter 列表。
不幸的是,C# 没有任何本机工具可以完全按照您的要求进行操作。
但是,我的 C# eval 程序确实允许评估 C# 代码。它提供了在运行时评估 C# 代码并支持许多 C# 语句。事实上,此代码可用于任何 .NET 项目,但仅限于使用 C# 语法。查看我的网站http://csharp-eval.com了解更多详细信息。
正确的答案是您需要缓存所有结果以保持低内存使用率。
一个例子看起来像这样
TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
}
并将其添加到列表
List<string> results = new List<string>();
for() results.Add(result);
保存id并在代码中使用
希望这可以帮助