2009-04-22 10 views
19

Tengo una expresión de llamada de método e intento invocar el método. Descubrí una manera, pero tengo problemas para recuperar los valores de los parámetros, ya que no todos los argumentos se describen con ConstantExpression.Cómo llamar al método desde MethodCallExpression en 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); 
} 

Además, he visto algunas otras maneras de invocar el método, ahora no estoy seguro de cuál es la forma correcta de hacerlo.

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

Así que mi pregunta es, ¿Cómo puedo recuperar los valores de los argumentos método de methodCallExpression.Arguments?

¿O hay una manera más fácil de lograr mi objetivo?

Respuesta

21

No necesita preocuparse por recuperar los argumentos y llamar al MethodInfo usted mismo, puede dejar que .NET lo haga por usted. Todo lo que necesita hacer es crear una expresión Lambda que contenga ese método.

por ejemplo.

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

Eso es cómo hacer frente a las consultas anidadas en mi proveedor de LINQ de todos modos.

EDITAR: En realidad, parece que ya tiene una LambdaExpression en la variable del selector. En ese caso, usted debería ser capaz de simplemente compilar e invocar directamente:

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

Gracias, eso es mucho más fácil. Lo estoy haciendo así ahora: // compilamos la expresión lambda para obtener el delegado para invocar. Acción action = selector.Compile(); // invocar la expresión en cada elemento dentro de la enumerable foreach (elemento de fuente de origen) { acción (elemento); } Y, finalmente, también encontré la documentación de msdn para este problema: http://msdn.microsoft.com/en-us/library/bb882536.aspx – Enyra

+0

¿Hay alguna razón por la que no puedas hacer 'selector.Compile()() '? ¿Por qué 'Invoke' o' DynamicInvoke' cuando funcionan los paréntesis? – ErikE

6

compilar una expresión es una operación muy intensiva, por lo que sólo haría que si usted está planeando en la reutilización de la expresión. Yo recomendaría la manera de reflexión de lo contrario; usted encontrará que se ejecuta más rápido. Nunca invoque expression.Compile() en un bucle cerrado.

2

@ Ch00k < - Gracias, buena explicación. Me gustaría agregar que

selector.Compile(); 

le da un delegado. Para un método de instancia necesita una instancia para llamar a este método. Se pasa este caso como el argumento de 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

me gustaría probar este para devolver el objeto:

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

Es mucho más rápido pueden llamar a los siguientes:

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

Si desea compilar su expresión.call en una Acción o Func, así es como lo hace:

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

Esto le permite simplemente hacer func.Invoke ("mystring") o func ("mi cadena");

El secreto aquí es que necesita pasar los mismos parámetros que usó cuando creó Expression.Call, de lo contrario se obtiene un error de tipo "InvalidOperationException" variable 's' de tipo 'System.String' al que se hace referencia desde el alcance '' , pero no está definido.

Cuestiones relacionadas