views:

1180

answers:

3

Hello,

I have a method call expression and try to invoke the method. I figured out a way, but I have problems in retrieving the parameter values since not every argument is described with a ConstantExpression.

Expression<Action<T>> = t => t.DoSomething(Par0, Par1, Par2);
MethodCallExpression methodCallExpression = selector.Body as MethodCallExpression;

// get the information which is needed to invoke the method from the provided lambda expression.
MethodInfo methodInfo = methodCallExpression.Method;
object[] arguments = methodCallExpression.Arguments.OfType<ConstantExpression>().Select(p => p.Value).ToArray();

// invoke the expression on every item within the enumerable
foreach (TSource item in source)
{ 
    methodInfo.Invoke(item, arguments);
}

Additionally, I have seen some other ways to invoke the method, now I'm not sure what is the right way to do it.

var func = expression.Compile();
var success = func.Invoke();

So my Question is, how can I retrieve the method argument values from "methodCallExpression.Arguments"? Or is there an easier way to achieve my goal?

+1  A: 

You don't need to worry about retrieving the arguments and calling the MethodInfo yourself, you can let .NET do it for you. All you need to do is create a Lambda expression containing that method.

eg.

MethodCallExpression expression = GetExpresionSomeHow();
object result = Expression.Lambda(expression).Compile().DynamicInvoke();

That's how I deal with nested queries in my Linq provider anyway.

EDIT: Actually, it looks like you might already have a LambdaExpression in the selector variable. In that case, you should be able to just compile and invoke it directly:

object result = selector.Compile().DynamicInvoke();
Ch00k
Thanks, that is much easier. I'm doing it like this now:// compile the lambda expression to get the delegate for invoking. Action<TSource> action = selector.Compile();// invoke the expression on every item within the enumerableforeach (TSource item in source){ action(item);}And finally I also found the msdn documentation for this issue: http://msdn.microsoft.com/en-us/library/bb882536.aspx
Enyra
+1  A: 

Compiling an expression is a very intensive operation, so I would only do that if you are planning on re-using the expression. I would recommend the reflection way otherwise; you will find it executes faster. Never call expression.Compile() in a tight loop.

+1  A: 

@Ch00k <-- Thanks, nice explanation. I would just like to add that

selector.Compile();

gives you a delegate. For an instance method you need an instance on which to call this method. You pass this instance as the argument to DynamicInvoke ala

// Grab the method from MyClass - param1 and param2 are the actual parameters you
// want to pass to the method call.
Expression<Func<MyClass, TValue>> selector = (x => x.MyMethod(param1, param2));

// Create an instance of MyClass to call the method on
var myClass = new MyClass();

// Call the method on myClass through DynamicInvoke
object returnValue = selector.Compile().DynamicInvoke(myClass);
B Tyler