2009-04-22 37 views
19

Ich habe eine Methode Aufruf Ausdruck und versuchen, die Methode aufzurufen. Ich habe einen Weg gefunden, aber ich habe Probleme beim Abrufen der Parameterwerte, da nicht jedes Argument mit einem ConstantExpression beschrieben wird.So rufen Sie die Methode von einer 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); 
} 

Zusätzlich habe ich einige andere Wege gesehen, die Methode aufzurufen, jetzt bin ich nicht sicher, was der richtige Weg, es zu tun ist.

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

Also meine Frage ist, wie kann ich die Methode Argument Werte von methodCallExpression.Arguments abrufen?

Oder gibt es einen einfacheren Weg, um mein Ziel zu erreichen?

Antwort

21

Sie müssen sich keine Gedanken über das Abrufen der Argumente und das Aufrufen von MethodInfo machen. Sie können .NET das für Sie erledigen lassen. Alles, was Sie tun müssen, ist einen Lambda-Ausdruck zu erstellen, der diese Methode enthält.

z.

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

So behandle ich verschachtelte Abfragen in meinem Linq-Provider sowieso.

EDIT: Eigentlich sieht es so aus, als ob Sie bereits eine LambdaExpression in der Selektorvariablen haben. In diesem Fall sollten Sie in der Lage sein, es zu einfach zu kompilieren und rufe direkt:

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

Danke, das ist viel einfacher. Ich mache es jetzt so: // kompilieren Sie den Lambda-Ausdruck, um den Delegaten zum Aufrufen zu erhalten. Aktion action = selector.Compile(); // den Ausdruck für jedes Element innerhalb des Enumerable aufrufen foreach (TSource-Element in der Quelle) { action (item); } Und schließlich habe ich auch die msdn Dokumentation für dieses Problem gefunden: http://msdn.microsoft.com/en-us/library/bb882536.aspx – Enyra

+0

Gibt es einen Grund, Sie können nicht einfach 'selector.Compile tun()() '? Warum 'Invoke' oder' DynamicInvoke' wenn Klammern funktionieren? – ErikE

6

Kompilieren Ausdruck ein sehr intensiver Betrieb ist, so würde ich das nur tun, wenn Sie auf der Wiederverwendung des Ausdruck planen. Ich würde den Reflexionsweg sonst empfehlen; Sie werden feststellen, dass es schneller ausgeführt wird. Rufen Sie expression.Compile() nie in einer engen Schleife auf.

2

@ Ch00k < - Danke, schöne Erklärung. Ich möchte nur hinzufügen, dass

selector.Compile(); 

gibt Ihnen einen Delegierten. Für eine Instanzmethode benötigen Sie eine Instanz, um diese Methode aufzurufen. Sie übergeben diese Instanz als Argument an 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

ich das versuchen würde, das Objekt zurück:

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 ist viel schneller können die folgenden Aufruf:

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

Wenn Sie möchten Ihre expression.call in eine Action oder Func kompilieren, so machen Sie das:

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

Damit können Sie einfach func.Invoke ("mystring") oder func ("my string");

Das Geheimnis hier ist, müssen Sie die gleichen Parameter übergeben, die Sie beim Erstellen der Expression.Call verwendet haben, andernfalls erhalten Sie eine Fehlermeldung vom Typ "InvalidOperationException" Variable 's' vom Typ 'System.String' vom Bereich '' verwiesen , aber es ist nicht definiert.

Verwandte Themen