内联评估代码(或使用反射)
evaluate code inline (or using reflection)
为了抓住问题的核心,我做了很多简化:
我有一个需要填充属性的控件。不幸的是,这些值都位于 CodeDom.CodeStatements 中。
所以我真的必须使用如下代码来获取值:
static string GenerateCode(CodeStatementCollection statements, Control control)
{
var writer = new StringWriter();
var compiler = new CSharpCodeProvider();
foreach (CodeStatement statement in statements)
{
var codeAssignStatement = statement as System.CodeDom.CodeAssignStatement;
var left = codeAssignStatement?.Left as System.CodeDom.CodePropertyReferenceExpression;
if (left == null)
{
continue;
}
var right = codeAssignStatement.Right as System.CodeDom.CodeObjectCreateExpression;
if (right == null)
{
continue;
}
var expressionWriter = new StringWriter();
compiler.GenerateCodeFromExpression(right,expressionWriter,null);
var expression = expressionWriter.ToString();
control.SetPropertyValue(left.PropertyName, expression);
compiler.GenerateCodeFromStatement(statement, writer, null);
}
return writer.ToString();
}
注意代码中使用了反射,SetPropertyValue实际上是一个扩展方法:
public static void SetPropertyValue(this object obj, string propName, object value)
{
obj.GetType().GetProperty(propName).SetValue(obj, value, null);
}
但这仅在值是文字值而不是表达式时有效。
在运行的时候,我从CodeDom语句中得到的是一个表达式,控件也是一个按钮。
Button.Location = New System.Drawing.Point(12,12);
所以在上面的代码示例中,表达式="New System.Drawing.Point(12,12)"
现在我不想 运行 通过一系列 IF 语句来首先确定类型。
是否可以使用反射根据表达式设置属性?
如果不是,是否可以在 .net 中使用某种 Eval 函数来完成此操作?
要从脚本创建代码,您需要动态编译该方法。
您要做的第一件事就是将方法编译成 MethodInfo
。
您可以创建一个方法来完成此操作。
public static MethodInfo CreateMethodInfo(string script, string className, string methodName)
{
using (var compiler = new CSharpCodeProvider())
{
var parms = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true,
ReferencedAssemblies = { "System.Drawing.dll" }
};
return compiler.CompileAssemblyFromSource(parms, script)
.CompiledAssembly.GetType(className)
.GetMethod(methodName);
}
}
完成此操作后,您可以像这样使用它。
var script = "public class Methods{public static System.Drawing.Point DoSomething(){return new System.Drawing.Point(12, 12);}}";
var method = CreateMethodInfo(script, "Methods", "DoSomething");
现在 method
变量保存了您的 MethodInfo
,因此您只需调用它即可。
Button.Location = (Point)method.Invoke(null, null);
我想你可以试试 LINQ 表达式。
https://msdn.microsoft.com/en-us/library/bb335710(v=vs.110).aspx
将您的方法更改为如下所示:
public static void SetPropertyValue(this object obj, string propName, Expression<Func<T, object>> value)
在表达式中配置和编译委托函数,并在您的 SetPropertyValue() 中调用它。
为了抓住问题的核心,我做了很多简化:
我有一个需要填充属性的控件。不幸的是,这些值都位于 CodeDom.CodeStatements 中。
所以我真的必须使用如下代码来获取值:
static string GenerateCode(CodeStatementCollection statements, Control control)
{
var writer = new StringWriter();
var compiler = new CSharpCodeProvider();
foreach (CodeStatement statement in statements)
{
var codeAssignStatement = statement as System.CodeDom.CodeAssignStatement;
var left = codeAssignStatement?.Left as System.CodeDom.CodePropertyReferenceExpression;
if (left == null)
{
continue;
}
var right = codeAssignStatement.Right as System.CodeDom.CodeObjectCreateExpression;
if (right == null)
{
continue;
}
var expressionWriter = new StringWriter();
compiler.GenerateCodeFromExpression(right,expressionWriter,null);
var expression = expressionWriter.ToString();
control.SetPropertyValue(left.PropertyName, expression);
compiler.GenerateCodeFromStatement(statement, writer, null);
}
return writer.ToString();
}
注意代码中使用了反射,SetPropertyValue实际上是一个扩展方法:
public static void SetPropertyValue(this object obj, string propName, object value)
{
obj.GetType().GetProperty(propName).SetValue(obj, value, null);
}
但这仅在值是文字值而不是表达式时有效。
在运行的时候,我从CodeDom语句中得到的是一个表达式,控件也是一个按钮。
Button.Location = New System.Drawing.Point(12,12);
所以在上面的代码示例中,表达式="New System.Drawing.Point(12,12)"
现在我不想 运行 通过一系列 IF 语句来首先确定类型。
是否可以使用反射根据表达式设置属性?
如果不是,是否可以在 .net 中使用某种 Eval 函数来完成此操作?
要从脚本创建代码,您需要动态编译该方法。
您要做的第一件事就是将方法编译成 MethodInfo
。
您可以创建一个方法来完成此操作。
public static MethodInfo CreateMethodInfo(string script, string className, string methodName)
{
using (var compiler = new CSharpCodeProvider())
{
var parms = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true,
ReferencedAssemblies = { "System.Drawing.dll" }
};
return compiler.CompileAssemblyFromSource(parms, script)
.CompiledAssembly.GetType(className)
.GetMethod(methodName);
}
}
完成此操作后,您可以像这样使用它。
var script = "public class Methods{public static System.Drawing.Point DoSomething(){return new System.Drawing.Point(12, 12);}}";
var method = CreateMethodInfo(script, "Methods", "DoSomething");
现在 method
变量保存了您的 MethodInfo
,因此您只需调用它即可。
Button.Location = (Point)method.Invoke(null, null);
我想你可以试试 LINQ 表达式。 https://msdn.microsoft.com/en-us/library/bb335710(v=vs.110).aspx
将您的方法更改为如下所示:
public static void SetPropertyValue(this object obj, string propName, Expression<Func<T, object>> value)
在表达式中配置和编译委托函数,并在您的 SetPropertyValue() 中调用它。