evaluate code inline (or using reflection)

2019-08-11 07:21发布

问题:

I've simplified this a lot to get to the core of the issue:

I have a control that needs properties populated. Unfortunately the values are all sitting in CodeDom.CodeStatements.

So I literally have to fish for the values by using code like this :

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();
      }

Notice that the code is using reflection, SetPropertyValue is actually an extension method:

public static void SetPropertyValue(this object obj, string propName, object value)
      {
         obj.GetType().GetProperty(propName).SetValue(obj, value, null);
      }

But this only works if the value is a literal value, not an expression.

At runtime, what I'm getting from the CodeDom statement is an expression, also the control is a button.

Button.Location = New System.Drawing.Point(12,12);

So in the above code sample, expression = "New System.Drawing.Point(12,12)"

Now I don't want to have to run through a series of IF statements to first determine the type.

Is it possible to use reflection to set a property based on an expression?

If not, is it possible to use some kind of Eval function in .net to pull this off?

回答1:

To create code from a script you would need to dynamically compile the method.

The first thing you would have to do is compile the method into a MethodInfo. You can create a method to accomplish this.

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);
    }
}

Once you have this done you can use it like this.

var script = "public class Methods{public static System.Drawing.Point DoSomething(){return new System.Drawing.Point(12, 12);}}";

var method = CreateMethodInfo(script, "Methods", "DoSomething");

Now that method variable holds your MethodInfo, so you just need to invoke it.

Button.Location = (Point)method.Invoke(null, null);


回答2:

I think you can try LINQ Expression. https://msdn.microsoft.com/en-us/library/bb335710(v=vs.110).aspx

Change you method to like below:

public static void SetPropertyValue(this object obj, string propName, Expression<Func<T, object>> value)

Configure and compile the delegate function in expression and call it in your SetPropertyValue().