如何从c#中的MethodCallExpression调用该方法

时间:2009-04-22 09:52:12

标签: c# .net lambda parameters

我有一个方法调用表达式并尝试调用该方法。我找到了一种方法,但是我在检索参数值方面遇到了问题,因为不是每个参数都用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);
}

此外,我已经看到了一些其他方法来调用该方法,现在我不确定这是什么方法。

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

所以我的问题是,如何从methodCallExpression.Arguments检索方法参数值?

或者有更简单的方法来实现我的目标吗?

5 个答案:

答案 0 :(得分:26)

您无需担心检索参数并自行调用MethodInfo,您可以让.NET为您执行此操作。您需要做的就是创建一个包含该方法的Lambda表达式。

例如

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

无论如何,这就是我在Linq提供商中处理嵌套查询的方式。

编辑:实际上,看起来你可能已经在选择器变量中有一个LambdaExpression。在这种情况下,您应该能够直接编译并调用它:

object result = selector.Compile().DynamicInvoke();

答案 1 :(得分:7)

编译表达式是一项非常密集的操作,所以如果您计划重新使用表达式,我只会这样做。否则我会推荐反射方式;你会发现它执行得更快。切勿在紧密循环中调用expression.Compile()。

答案 2 :(得分:1)

@ Ch00k&lt; - 谢谢,很好的解释。我只想补充一点

selector.Compile();

给你一个代表。对于实例方法,您需要一个实例来调用此方法。您将此实例作为参数传递给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);

答案 3 :(得分:1)

如果要将expression.call编译为Action或Func,请执行以下操作:

var method = typeof(MyType).GetMethod(nameof(MyType.MyMethod), BindingFlags.Public | BindingFlags.Static);
var parameter = Expression.Parameter(typeof(string), "s");
var call = Expression.Call(method, parameter);
var lambda = Expression.Lambda<Func<string, int>>(call, call.Arguments.OfType<ParameterExpression>());
var func = lambda.Compile();
int result = func("sample string input");

这允许你简单地执行func.Invoke(&#34; mystring&#34;)或func(&#34; my string&#34;);

这里的秘诀是你需要传递你在创建Expression.Call时使用的相同参数,否则你会收到类型&#34; InvalidOperationException&#34;的错误。变量&#39; s&#39;类型&#39; System.String&#39;引用范围&#39;&#39;,但未定义。

答案 4 :(得分:0)

我会尝试这个来返回对象:

private static object _getValue(MethodCallExpression expression)
{
    var objectMember = Expression.Convert(expression, typeof(object));

    var getterLambda = Expression.Lambda<Func<object>>(objectMember);

    var getter = getterLambda.Compile();

    return getter();
}

调用以下内容要快得多:

LambdaExpression l = Expression.Lambda(Expression.Convert(element, element.Type));
return l.Compile().DynamicInvoke();
相关问题