2009-05-24 13 views
2

Update: Dies funktioniert, ich wäre blöd :(Parameter Problem mit Expression.Lambda()

Ich habe folgende Erweiterungsmethode

public static string ExtMethod(this object self, object myparameter); 

zur Laufzeit dieses in einem beliebigen Zahl genannt wird Möglichkeiten, Wege, ich glaube, das sind alles Möglichkeiten:

Expression<Func<T, string>> expr = x => x.property.ExtMethod(5); 
Expression<Func<T, string>> expr = x => x.property.ExtMethod(new object()); 
Expression<Func<T, string>> expr = x => x.property.ExtMethod(someMethod()); 
Expression<Func<T, string>> expr = x => x.property.ExtMethod(x.someMethod()); 
Expression<Func<T, string>> expr = x => x.property.ExtMethod(x.OtherProperty); 

, was ich die „myparameter“ tun müssen, ist zu bewerten, da "expr "und eine" T "

wegen der beiden Fälle, in denen x in myparameter verwendet wird, ich dachte, ich brauchte einen Delegierten der Form zu erstellen:

Expression<Func<T, object>> expr = x => [myparameter expression here] 

i gedacht funktionieren würde:

var extMethodExpr = expr.Body as MethodCallExpression; 
var myparameterExpr = extMethodExpr.Arguments[1]; 

var myparam = Expression.Lambda(myparameterExpr, expr.Parameters).Compile().Invoke(someT) 

aber für die Ausdrücke, die x nicht verbunden, i erhalten TargetParameterCountException :(

in diesen Fällen, wenn ich tun:

var myparam = Expression.Lambda(myparameterExpr).Compile().Invoke(someT) 

es funktioniert gut.

Wie löse ich das?

dank

+0

Das ist hardcore ... Kann immer noch nicht verstehen, was das Problem ist. :) –

+0

Erweiterungsmethoden auf Objekt sind selten eine gute Idee; und (Pedant) du erstellst einen Ausdrucksbaum (kein Delegierter) - aber schau jetzt ... –

+0

@Marc das ist nur Pseudocode;) –

Antwort

6

OK; bin auf den Grund gegangen; in der Zeile:

var myparam = Expression.Lambda(myparameterExpr).Compile().Invoke(someT); 

Wenn Sie nicht in einem someT zu passieren versuchen, dies für jenen Ausdrücke funktionieren würde, die nicht x im Argumente einzubeziehen; diejenigen, für die das tun, müssen Sie die Lambda sagen, den Parameter (das gleiche von der ursprünglichen Lambda) enthalten - einfach durch:

var myparam = Expression.Lambda(myparameterExpr, 
      outerLambda.Parameters[0]).Compile().Invoke(someT); 

Hier einige Arbeitscode, der die inneren Parameter auswertet (eine Instanz des Gegebenen Argumenttyp); Beachten Sie, dass ich den Parameter verwenden, auch wenn es nicht eine x involvieren - sonst, was würde es mit der Instanz tun?

using System; 
using System.Linq.Expressions; 
using System.Reflection; 
class Foo { 
    public string Bar {get;set;} 
    public int someMethod() { return 4; } 
    public int OtherProperty { get { return 3; } } 
} 
static class Program 
{ 
    static int someMethod() { return 3; } 
    static void Main() 
    { 
     Foo foo = new Foo(); 
     Test<Foo>(x => x.Bar.ExtMethod(5), foo); 
     Test<Foo>(x => x.Bar.ExtMethod(new object()), foo); 
     Test<Foo>(x => x.Bar.ExtMethod(someMethod()), foo); 
     Test<Foo>(x => x.Bar.ExtMethod(x.someMethod()), foo); 
     Test<Foo>(x => x.Bar.ExtMethod(x.OtherProperty), foo); 
    } 
    static void Test<T>(Expression<Func<T, string>> expr, T instance) 
    { 
     if (expr.Body.NodeType != ExpressionType.Call) 
     { 
      throw new InvalidOperationException("Call expected"); 
     } 
     var call = ((MethodCallExpression)expr.Body); 
     if (call.Method != typeof(Program).GetMethod(
      "ExtMethod", BindingFlags.Static | BindingFlags.NonPublic)) 
     { 
      throw new InvalidOperationException("ExtMethod expected"); 
     } 
     // we know that ExtMethod has 2 args; pick myParameter (the 2nd); 
     // then build an expression over arg, re-using our outer param 
     var newLambda = Expression.Lambda<Func<T, object>>(
      call.Arguments[1], expr.Parameters[0]); 

     // evaluate it and show the argument value 
     object value = newLambda.Compile()(instance); 
     Console.WriteLine(value); 
    } 
    static string ExtMethod(this object self, object myParameter) { 
     return self.ToString(); 
    } 
} 
+0

Ok, ich bin ein kompletter Idiot. Mein Code oben funktioniert. In meinem echten Code habe ich kein T an lambda.invoke() übergeben, daher die TargetParameterCountException. Do! –

+0

haben eine +1 und + Antwort für Ihre Mühe, geschätzt :) Hat mir geholfen, den Fehler zu finden! –

0

Was passiert, wenn Sie expr.Parameters.Count überprüfen und wenn es 0 ist, aufrufen, ohne die Parameter?

+0

Ich habe nur Ausdruck als Ausdruck, der keine .Parameters hat.Wie kann ich elegant darauf zugreifen? –

Verwandte Themen