2013-09-06 8 views
13

ich die folgende KlasseErhalten Sie den Parameterwert aus einer Linq Expression

public class MyClass 
{ 
    public bool Delete(Product product) 
    { 
     // some code. 
    } 
} 

Jetzt habe ich eine Hilfsklasse, die wie dieser Code zu verwenden, um von irgendwo

public class Helper<T, TResult> 
{ 

    public Type Type; 
    public string Method; 
    public Type[] ArgTypes; 
    public object[] ArgValues; 

    public Helper(Expression<Func<T, TResult>> expression) 
    { 
     var body = (System.Linq.Expressions.MethodCallExpression)expression.Body; 

     this.Type = typeof(T); 
     this.Method = body.Method.Name; 
     this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray(); 
     this.ArgValues = ??? 
    } 
} 

Die Idee ist aus:

// I am returning a helper somewhere 
public Helper<T> GetMethod<T>() 
{ 
    var product = GetProduct(1); 
    return new Helper<MyClass>(x => x.Delete(product)); 
} 

// some other class decides, when to execute the helper 
// Invoker already exists and is responsible for executing the method 
// that is the main reason I don't just comile and execute my Expression 
public bool ExecuteMethod<T>(Helper<T> helper) 
{ 
    var instance = new MyClass(); 
    var Invoker = new Invoker(helper.Type, helper.Method, helper.ArgTypes, helper.ArgValues); 
    return (bool)Invoker.Invoke(instance); 
} 

der Punkt, wo ich stecken bin, ist, wie die Argumente aus dem Ausdruck selbst zu extrahieren.

fand ich diese Art und Weise

((ConstantExpression)((MemberExpression)body.Arguments[0]).Expression).Value 

, die ein Objekttyp mit einem Feld „Produkt“ zu sein scheint, aber ich glaube, es muss eine einfachere Lösung sein.

Irgendwelche Vorschläge.

aktualisieren

Nur um zu klären, ich meinen Code modifiziert nach, was ich achive wollen. In meiner realen Wort Anwendung habe ich schon eine Klasse, die das gleiche tut, aber ohne einen Ausdruck Baum:

var helper = new Helper(typeof(MyClass), "Delete", 
    new Type[] { typeof(Product) }, new object[] {product})); 

Der Hauptgrund für meine Helper<T> ist die Überprüfung Compile-Zeit zu haben, wenn die Methode Signatur gültig ist.

Update 2

Dies ist meine aktuelle Implementierung ist, gibt es einen besseren Weg, um die Werte zu den acces, ohne Reflexion mit?

+5

gibt es keinen Grund, warum Sie können nicht nur die kompilieren Ausdruck und führen Sie es aus? Oder macht das nur Spaß, um Ausdrucksbäume zu lernen? –

+1

@rbev +1 schlag mich dazu. – Aron

+0

Hey, warum nicht einfach direkt 'Delete (product)' aufrufen? – Aron

Antwort

13

Diese Methode funktioniert ziemlich gut. Es gibt die Argumenttypen und Werte für eine Expression>

private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression) 
    { 
     var body = (System.Linq.Expressions.MethodCallExpression)expression.Body; 
     var values = new List<KeyValuePair<Type, object>>(); 

     foreach (var argument in body.Arguments) 
     { 
      var exp = ResolveMemberExpression(argument); 
      var type = argument.Type; 

      var value = GetValue(exp); 

      values.Add(new KeyValuePair<Type, object>(type, value)); 
     } 

     return values.ToArray(); 
    } 

    public static MemberExpression ResolveMemberExpression(Expression expression) 
    { 

     if (expression is MemberExpression) 
     { 
      return (MemberExpression)expression; 
     } 
     else if (expression is UnaryExpression) 
     { 
      // if casting is involved, Expression is not x => x.FieldName but x => Convert(x.Fieldname) 
      return (MemberExpression)((UnaryExpression)expression).Operand; 
     } 
     else 
     { 
      throw new NotSupportedException(expression.ToString()); 
     } 
    } 

    private static object GetValue(MemberExpression exp) 
    { 
     // expression is ConstantExpression or FieldExpression 
     if (exp.Expression is ConstantExpression) 
     { 
      return (((ConstantExpression)exp.Expression).Value) 
        .GetType() 
        .GetField(exp.Member.Name) 
        .GetValue(((ConstantExpression)exp.Expression).Value);  
     } 
     else if (exp.Expression is MemberExpression) 
     { 
      return GetValue((MemberExpression)exp.Expression); 
     } 
     else 
     { 
      throw new NotImplementedException(); 
     } 
    } 
+0

Ich glaube, Sie bedeuten (hinzugefügt, um eine fehlende Typargument): 'KeyValuePair [] ResolveArgs (Expression > expression)' –

+0

@PatrickKoorevaar Du hast Recht. In meinem Code befindet sich die Methode in einer generischen Klasse mit dem Argument type, also habe ich diesen Teil verpasst. –

0

Hier ist ein Beispiel für die Erstellung eines Delegaten mit einem Lambda. Die Objektinstanz wird mithilfe einer C# -Funktion namens Closure in den Delegaten eingekapselt.

Alternativ versuchen Sie, einen Helper automatisch Currys zu erstellen.

public class Helper<T> 
    where T : new() 
{ 
    public TResult Execute<TResult>(Func<T, TResult> methodLambda) 
    { 
     var instance = new T(); 
     return methodLamda(instance); 
    } 
} 

public void Main() 
{ 
    var helper = new Helper<MyClass>(); 
    var product = new Product(); 
    helper.Execute(x => x.Delete(product)); 
} 

Allerdings habe ich dieses Problem zu sagen, sieht verdächtig wie die Schaffung einer Hilfsklasse die Lebensdauer eines WCF-Proxy zu behandeln .... Sie wissen schon ... nur sagen, ... wobei diese ISN "Wie würde ich das angehen ... einfach weil dieser Ansatz WCF-spezifischen Code in Ihre Domain ausgibt.

+0

Das ist total nicht was ich will. –

6

Sie können das Argument Ausdruck kompilieren und rufen Sie es dann um den Wert zu berechnen:

var values = new List<object>(); 
foreach(var arg in body.Arguments) 
{ 
    var value = Expression.Lambda(argument).Compile().DynamicInvoke(); 
    values.Add(value); 
} 
this.ArgValues = values.ToArray(); 
+0

Habe es noch nicht probiert aber sieht vielversprechend aus. Vielen Dank. –

+1

Argumente sollten Arg sein wie in foreach erklärt. – swestner

+0

In [dieser] (https://blogs.msdn.microsoft.com/csharpfaq/2010/03/11/how-can-i-get-objects-and-property-values-from-expression-trees/) artice erwähnt, dass kompilieren nicht schnell ist. –

Verwandte Themen