2009-04-22 5 views
19

У меня есть выражение вызова метода и пытаюсь вызвать метод. Я понял способ, но у меня проблемы с получением значений параметров, так как не каждый аргумент описывается с помощью ConstantExpression.Как вызвать метод из MethodCallExpression в C#

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?

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

ответ

21

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

например.

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

Вот как я в любом случае имею дело с вложенными запросами в моем поставщике Linq.

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

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

Спасибо, это намного проще. Я делаю это следующим образом: // скомпилируем выражение лямбда, чтобы получить делегат для вызова. Действие action = selector.Compile(); // вызывать выражение для каждого элемента в перечисляемом foreach (элемент источника в источнике) { action (item); } И, наконец, я также нашел документацию по msdn для этой проблемы: http://msdn.microsoft.com/en-us/library/bb882536.aspx – Enyra

+0

Есть ли причина, по которой вы не можете просто сделать 'selector.Compile()() '? Почему 'Invoke' или' DynamicInvoke' при работе круглых скобок? – ErikE

6

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

2

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

selector.Compile(); 

дает вам делегата. Для метода экземпляра вам нужен экземпляр для вызова этого метода. Вы передаете этот экземпляр в качестве аргумента DynamicInvoke ала

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

If вы хотите скомпилировать выражение 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» variable 's' типа «System.String», ссылки из области действия '' , но он не определен.

Смежные вопросы