Подтвердить что ты не робот

Как вызвать метод из MethodCallExpression в С#

У меня есть выражение вызова метода и пытаюсь вызвать метод. Я понял способ, но у меня проблемы с получением значений параметров, поскольку не каждый аргумент описывается с помощью 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?

Или есть более простой способ достичь моей цели?

4b9b3361

Ответ 1

Вам не нужно беспокоиться о том, как извлекать аргументы и вызывать метод MethodInfo самостоятельно, вы можете позволить .NET сделать это за вас. Все, что вам нужно сделать, это создать выражение Lambda, содержащее этот метод.

например.

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

Так как я все-таки имею дело с вложенными запросами в моем поставщике Linq.

EDIT: На самом деле, похоже, что у вас может быть LambdaExpression в переменной селектора. В этом случае вы должны иметь возможность просто компилировать и вызывать его непосредственно:

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

Ответ 2

Компиляция выражения - очень интенсивная операция, поэтому я бы сделал это только в том случае, если вы планируете повторно использовать выражение. Я бы порекомендовал это иначе; вы обнаружите, что он выполняется быстрее. Никогда не вызывайте выражение .Compile() в замкнутом цикле.

Ответ 3

@Ch00k < - Спасибо, хорошее объяснение. Я хотел бы добавить, что

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

Ответ 4

Если вы хотите скомпилировать выражение 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( "mystring" ) или func ( "моя строка" );

Секрет здесь заключается в том, что вам нужно передать те же параметры, которые вы использовали при создании Expression.Call, иначе вы получите ошибку типа "InvalidOperationException" переменной 's' типа "System.String", на которую ссылаются из области действия '', но он не определен.

Ответ 5

Я бы попробовал это, чтобы вернуть объект:

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