【发布时间】:2021-12-27 11:15:31
【问题描述】:
我想做相当于:
object result = Eval("1 + 3");
string now = Eval("System.DateTime.Now().ToString()") as string
在 Biri s link 之后,我得到了这个 sn-p(修改以删除过时的方法 ICodeCompiler.CreateCompiler():
private object Eval(string sExpression)
{
CSharpCodeProvider c = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("system.dll");
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
StringBuilder sb = new StringBuilder("");
sb.Append("using System;\n");
sb.Append("namespace CSCodeEvaler{ \n");
sb.Append("public class CSCodeEvaler{ \n");
sb.Append("public object EvalCode(){\n");
sb.Append("return " + sExpression + "; \n");
sb.Append("} \n");
sb.Append("} \n");
sb.Append("}\n");
CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString());
if (cr.Errors.Count > 0)
{
throw new InvalidExpressionException(
string.Format("Error ({0}) evaluating: {1}",
cr.Errors[0].ErrorText, sExpression));
}
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;
}
【问题讨论】:
-
请记住,除非您在单独的 AppDomain 中进行编译和执行,否则您可能会遇到内存问题。以这种方式生成的程序集无法卸载,但如果您在单独的 AppDomain 中创建程序集,则可以卸载 AppDomain 从而卸载生成的程序集。
-
为什么这是必要的?我高度怀疑这是否是您想要的任何目的的最佳设计。很有可能还有其他机制可以产生您想要的结果,而无需打开在运行时评估代码 sn-ps 的大量蠕虫(出于安全性和可维护性)。
-
我想要一种快速而肮脏的方法来将表达式验证添加到我编写的 DSL 中。我控制提供给评估程序的文件,因此永远不会打开蠕虫罐;)另外,我只允许一个表达式,不要添加任何命名空间/引用任何程序集。这也应该阻止我自己造成伤害!
-
我在测试场景中这样做。我有一个程序集,它会在更新时更改版本号。我希望我的测试代码“后期绑定”到程序集 - 以便能够加载程序集并动态调用它。这是一个简单的反思问题。但是当我想实现一个在程序集中指定的接口时,它需要动态编译代码,因为接口本身是强命名的(带有版本号)。当我想调用 v1.3 时,我无法拥有实现 IFoo v1.2 的代码。动态编译解决了这个问题。
-
我应该说,还有很多其他的场景。这不是主流的事情,但是动态代码编译有很多场景是有意义的。
标签: c# reflection eval