2010-11-30 9 views
6

Ich möchte eine Reihe von Filtern für einen Benutzer zur Verfügung stellen, und jeder Filter entspricht einer Expression<Func<X, bool>>. Daher möchte ich vielleicht eine dynamische Liste verfügbarer Elemente ("Joe", "Steve", "Pete" usw.) erstellen und eine Sammlung von "fest codierten" Filtern basierend auf diesen Namen erstellen und den Benutzer auswählen lassen welchen Filter er benutzen möchte. Mein Problem ist, dass, selbst wenn ich versuche, meinen Ausdruck basierend auf einem String-Wert aus der dynamischen Liste zu "codieren", der Ausdruck den Wert immer noch als eine Eigenschaft speichert, die an einem anonymen Typ hängt (und Ich weiß nicht, wie man den Anon serialisiert. Tut mir leid, wenn das verwirrend ist, ich bin nicht ganz sicher, wie ich das artikulieren soll.LINQ: Wie erzwinge eine wertorientierte Referenz?

Hier ist mein Beispielcode:

public class Foo 
    { 
     public string Name { get; set; } 
    } 
    static void Main(string[] args) 
    { 
     Foo[] source = new Foo[] 
      { 
       new Foo() { Name = "Steven" } , 
       new Foo() { Name = "John" } , 
       new Foo() { Name = "Pete" }, 
      }; 

      List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>(); 
      foreach (Foo f in source) 
      { 
       Expression<Func<Foo, bool>> exp = x => x.Name == f.Name; 
       filterLst.Add(exp); 
      } 
    } 
} 

Mein Problem ist, dass, wenn ich sehe, wenn ich am Körper meines Ausdruck aussehen, es lautet wie folgt:

(x.Name = value(ConsoleApplication1.Program+<>c__DisplayClass3).value) 

Wenn das, was ich wirklich wollen, ist für die erste wie folgt zu lesen:

(x.Name = "Steven") 

(wenn ich meinen Code dies zu ändern, statt, das ist genau ly, was ich bekommen:

 Expression<Func<Foo, bool>> exp = x => x.Name == "Steven"; 

)

Ich habe versucht, meinen Wert auf einen lokalen String-Wert zu zwingen, bevor sie in die Expression kleben, aber es scheint nicht zu helfen:

List<Expression<Func<Foo, bool>>> filterLst = new List<Expression<Func<Foo, bool>>>(); 
    foreach (Foo f in source) 
    { 
     string value = f.Name; 
     Expression<Func<Foo, bool>> exp = x => x.Name == value; 
     filterLst.Add(exp); 
    } 

Ich verstehe nicht warum (oder wirklich, wie), es sieht immer noch an einem anonymen Typ, auch wenn ich eine lokale Variable verwende, die zu einem String deklariert wird. Gibt es eine Möglichkeit, dies so zu machen, wie ich es möchte?

+0

(aktualisierte Antwort zum Anzeigen des Ausdrucksbaumaufbaus) –

+0

Sie vereinfachen ein komplexeres Szenario für uns? Es gibt einfachere Möglichkeiten, nach einem Array von Objekteigenschaften zu filtern. –

+0

@Jonas: Ja, ich habe mein aktuelles Szenario ziemlich vereinfacht. Ich möchte nicht wirklich die Eigenschaft Name gegen eine konstante Zeichenfolge filtern. Ich beschäftige mich mit komplexeren Objekten und biete dem Benutzer mehrere Sammlungen von "Filtern" an, die auf verschiedenen Eigenschaften oder mehreren Eigenschaftsbedingungen basieren können. Letztendlich werden jedoch alle diese "Filter" zum selben Objekttyp abstrahiert (Ausdruck ), den ich zu einer IQueryable-Quellenliste hinzufüge, um schließlich eine Abfrage zu erzeugen. – Steven

Antwort

7

Der Anon-Typ ist eigentlich der vom Compiler generierte Typ, der verwendet wird, um Capture der Variablen durchzuführen. Mit Delegierten können Sie dies umgehen, indem Sie die Erfassung von Hand implementieren, aber nicht mit Lambda-Ausdrücke kompiliert zu Ausdrucksbäumen.

zwei Möglichkeiten:

  • build der Ausdrucksbaum explizit auf Code über Expression.Constant etc
  • lernen, wie die anon-Typen zu behandeln

Letztere eigentlich gar nicht so schlecht ist; Sie sind in der Regel nur MemberExpression, obwohl ich etwas Code herumstreife, der dies detailliert abdeckt. Ich kann auch Beispiele für die Erstellung des Ausdrucksbaums liefern, aber ich bin im Moment nicht an einem PC und es eignet sich nicht gut für iPod Typisierung ...

Von einem kurze lesen Sie die Frage I schau dir die erste Möglichkeit mehr an als die zweite.

Oh, und pass auf;)


bearbeiten, der erste Code foreach in der Frage nach dem berüchtigten l-Wert-Capture-Problem anfällig aussieht: Ich habe einen PC gefunden; p

 var param = Expression.Parameter(typeof(Foo), "x"); 
     var body = Expression.Equal(
      Expression.PropertyOrField(param, "Name"), 
      Expression.Constant(f.Name, typeof(string))); 

     var exp = Expression.Lambda<Func<Foo, bool>>(body, param); 
     filterLst.Add(exp); 
+0

Das sieht so aus, als müsste ich es tun, aber ich hatte gehofft, das Lambda zu nehmen und es direkt zu generieren ... Ich denke, ein wenig Arbeit bringt mich nicht um :) Was ist das berüchtigte I-Value Capture Problem? – Steven

+0

@Steven - im ersten Code-Block, den du gibst, erwarte ich * du findest alle 3 Filter gegen '" Pete "'. Weil Sie die * Variable * (nicht den * Wert * der Variablen) erfassen und die Schleifenvariable (technisch) * außerhalb der Schleife deklariert und daher von allen 3 Ausdrücken geteilt wird. –

+0

Danke für die Antwort. Ich musste ein bisschen darüber nachdenken, aber ich denke, ich verstehe. Wenn ich richtig bin, dann ist das Problem, dass ich angenommen habe, dass jede Iteration eine andere Version von diesem erhalten wird: value (ConsoleApplication1.Program + <> c__DisplayClass3) (vielleicht __DisplayClass4, __DisplayClass5?), Aber dieser Verweis verweist auf meinen "f" Referenz, die, sobald die Schleife fertig ist, immer den Namen "Pete" hat. Das hätte ich nicht bemerkt, bis es mich schon lange gebissen hat, also schätze ich die Warnung. – Steven

4

Marc GRA Antwort richtig ist, und Hier ist, wie Sie seine erste Wahl implementieren:

Dies ist jedoch in der Regel nicht erforderlich. Ihr zweites Beispiel für die for-Schleife sollte mit allen wichtigen LINQ-Anbietern funktionieren. Gibt es einen Grund, warum Sie den Ausdruck verwenden müssen, um Konstanten zu verwenden?

+0

(siehe auch die Bearbeitung, die ich eine Weile vorher hinzugefügt habe ...) –

+0

Ja, das habe ich nach dem Posten bemerkt. : P – StriplingWarrior

+0

Ich habe dir immer noch Requisiten für deine Mühe gegeben;) – Steven

Verwandte Themen