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

时间:2022-06-14 14:36:26

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.

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

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

Or is there an easier way to achieve my goal?

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

5 个解决方案

#1


23  

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.

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

eg.

例如。

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

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

这就是我在Linq提供程序中处理嵌套查询的方法。

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:

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

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

#2


7  

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.

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

#3


2  

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

@ Ch00k < - 谢谢,很好的解释。我想补充一点

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

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

#4


1  

If you want to compile your expression.call into a Action or Func, this is how you do:

如果要将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");

This allows you to simply do func.Invoke("mystring") or func("my string");

这允许你简单地执行func.Invoke(“mystring”)或func(“my string”);

The secret here is you need to pass the same parameters you used when creating the Expression.Call, otherwise you get an error of type "InvalidOperationException" variable 's' of type 'System.String' referenced from scope '', but it is not defined.

这里的秘诀是你需要传递你在创建Expression.Call时使用的相同参数,否则你会得到类型为“System.String”的类型为“InvalidOperationException”的变量''的错误,但是它是没有定义的。

#5


0  

I would try this to return the object:

我会尝试这个返回对象:

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

It is much faster can calling the following:

可以更快地调用以下内容:

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

#1


23  

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.

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

eg.

例如。

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

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

这就是我在Linq提供程序中处理嵌套查询的方法。

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:

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

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

#2


7  

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.

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

#3


2  

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

@ Ch00k < - 谢谢,很好的解释。我想补充一点

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

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

#4


1  

If you want to compile your expression.call into a Action or Func, this is how you do:

如果要将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");

This allows you to simply do func.Invoke("mystring") or func("my string");

这允许你简单地执行func.Invoke(“mystring”)或func(“my string”);

The secret here is you need to pass the same parameters you used when creating the Expression.Call, otherwise you get an error of type "InvalidOperationException" variable 's' of type 'System.String' referenced from scope '', but it is not defined.

这里的秘诀是你需要传递你在创建Expression.Call时使用的相同参数,否则你会得到类型为“System.String”的类型为“InvalidOperationException”的变量''的错误,但是它是没有定义的。

#5


0  

I would try this to return the object:

我会尝试这个返回对象:

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

It is much faster can calling the following:

可以更快地调用以下内容:

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