2009-04-22 15 views
19

Ho un'espressione di chiamata di metodo e provo a richiamare il metodo. Ho trovato un modo, ma ho problemi nel recuperare i valori dei parametri poiché non tutti gli argomenti sono descritti con una espressione costante.Come chiamare il metodo da MethodCallExpression in 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); 
} 

Inoltre, ho visto alcuni altri modi per richiamare il metodo, ora io non sono sicuro di quello che è il modo giusto per farlo.

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

Quindi la mia domanda è: come posso recuperare i valori metodo degli argomenti da methodCallExpression.Arguments?

O c'è un modo più semplice per raggiungere il mio obiettivo?

risposta

21

Non è necessario preoccuparsi di recuperare gli argomenti e chiamare da soli il metodo MethodInfo, è possibile consentire a .NET di farlo al posto tuo. Tutto ciò che devi fare è creare un'espressione Lambda che contenga quel metodo.

es.

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

Questo è il modo in cui gestisco comunque le query annidate nel mio provider Linq.

MODIFICA: in realtà, sembra che tu possa già avere LambdaExpression nella variabile di selezione. In tal caso, si dovrebbe essere in grado di compilare solo e richiamare direttamente:

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

Grazie, è molto più semplice. Lo sto facendo ora: // compila l'espressione lambda per ottenere il delegato per il richiamo. Azione action = selector.Compile(); // invocare l'espressione su ogni elemento all'interno dell'enumerabile foreach (articolo TSource nell'origine) { azione (elemento); E infine ho trovato anche la documentazione di msdn per questo problema: http://msdn.microsoft.com/en-us/library/bb882536.aspx – Enyra

+0

C'è un motivo per cui non si può semplicemente fare 'selector.Compile()() '? Perché "Invoke" o "DynamicInvoke" quando le parentesi funzionano? – ErikE

6

compilazione di un espressione è un'operazione molto intenso, quindi vorrei farlo solo se si prevede di riutilizzare l'espressione. Suggerirei diversamente il modo di riflessione; lo troverai eseguito più velocemente. Non chiamare mai expression.Compile() in un ciclo stretto.

2

@ Ch00k < - Grazie, bella spiegazione. Vorrei solo aggiungere che

selector.Compile(); 

ti dà un delegato. Per un metodo di istanza hai bisogno di un'istanza su cui chiamare questo metodo. Si passa questo caso come argomento 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); 
0

vorrei provare questo per restituire l'oggetto:

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

È molto più veloce in grado di chiamare il seguente:

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

Se vuoi compilare expression.call in un'azione o Func, ecco come procedere:

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

Ciò consente di fare semplicemente func.Invoke ("mystring") o func ("my string");

Il segreto qui è che è necessario passare gli stessi parametri utilizzati durante la creazione di Expression.Call, altrimenti si ottiene un errore di tipo "InvalidOperationException" variabile 'di tipo' System.String 'referenziato dall'ambito' ' , ma non è definito.

Problemi correlati