2009-05-24 9 views
2

Mise à jour: Cela marche, j'étais stupide :(problème de paramètres avec Expression.Lambda()

j'ai la méthode d'extension suivante

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

lors de l'exécution ce que l'on appelle dans un certain nombre des moyens façons, je pense que ce sont toutes les possibilités:

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

ce que je dois faire est d'évaluer la « myparameter », étant donné "expr « et un » T "

à cause des deux cas où x est utilisé dans myparameter, je pensais que je devais créer un délégué de la forme:

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

Je pensais que cela fonctionnerait:

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

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

mais pour les expressions qui ne concernent pas x, i get TargetParameterCountException :(

dans ces cas, si je fais:

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

il fonctionne très bien.

Comment résoudre ce problème?

grâce

+0

C'est hardcore ... Je n'arrive toujours pas à comprendre quel est le problème. :) –

+0

Les méthodes d'extension sur objet sont rarement une bonne idée; et (pedant) vous créez un arbre d'expression (pas un délégué) - mais en regardant maintenant ... –

+0

@Marc c'est juste pseudo code;) –

Répondre

6

OK; aller au fond de celui-ci; dans la ligne:

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

Si vous essayez de ne pas passer dans un someT, cela fonctionnerait pour les expressions qui ne concernent pas x dans l'argument; pour ceux qui le font, vous devez indiquer lambda pour inclure le paramètre (le même que celui du lambda d'origine) - simplement par:

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

Voici un code de travail qui évalue le paramètre interne (donné une instance de la type d'argument); notez que j'utilise le paramètre même si ne fait pas impliquer un x - sinon, que ferait-il avec l'instance?

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, je suis un idiot complet. Mon code ci-dessus fonctionne. Dans mon vrai code, je ne passais pas un T à lambda.invoke(), d'où le TargetParameterCountException. Doh! –

+0

avoir une +1 et + réponse pour votre effort, apprécié :) m'a aidé à trouver le bug! –

0

si vous vérifiez expr.Parameters.Count et si elle est 0, invoquez sans les paramètres?

+0

J'ai seulement expr comme une expression, qui n'a pas de paramètres.Comment puis-je y accéder avec élégance? –

Questions connexes