2010-09-30 2 views
5

Nach dem Lesen "Odd query expressions" von Jon Skeet, habe ich den folgenden Code ausprobiert. Ich erwartete, dass die LINQ-Abfrage am Ende in int query = proxy.Where(x => x).Select(x => x); übersetzt wird, die nicht kompiliert, da Where eine int zurückgibt. Der kompilierte Code gibt "Where (x => x)" an den Bildschirm aus und die Abfrage wird auf 2 gesetzt. Select wird nie aufgerufen, muss jedoch für die Kompilierung des Codes vorhanden sein. Was ist los?Warum kompiliert diese LINQ-Abfrage?

using System; 
using System.Linq.Expressions; 

public class LinqProxy 
{ 
    public Func<Expression<Func<string,string>>,int> Select { get; set; } 
    public Func<Expression<Func<string,string>>,int> Where { get; set; } 
} 

class Test 
{ 
    static void Main() 
    { 
     LinqProxy proxy = new LinqProxy(); 

     proxy.Select = exp => 
     { 
      Console.WriteLine("Select({0})", exp); 
      return 1; 
     }; 
     proxy.Where = exp => 
     { 
      Console.WriteLine("Where({0})", exp); 
      return 2; 
     }; 

     int query = from x in proxy 
        where x 
        select x; 
    } 
} 
+0

Siehe auch http://blogs.msdn.com/b/ericlippert/archive/2008/05/12/trivial-projections-are-usus-optimized-away.aspx für ein paar zusätzliche Gedanken zu dieser Funktion. –

Antwort

11

Es ist, weil Ihr „x wählen Sie“ ist in Wirklichkeit eine no-op - der Compiler nicht stört den Select(x => x) Anruf am Ende setzen. Es würde, wenn Sie die where Klausel obwohl entfernt. Ihre aktuelle Abfrage ist als degenerierter Abfrageausdruck bekannt. Weitere Informationen finden Sie in Abschnitt 7.16.2.3 der C# 4-Spezifikation. Insbesondere:

Ein degenerierter Abfrageausdruck ist einer, der trivial die Elemente der Quelle auswählt. Eine spätere Phase der Übersetzung entfernt degenerierte Abfragen, die durch andere Übersetzungsschritte eingeführt werden, indem sie durch ihre Quelle ersetzt werden. Es ist jedoch wichtig sicherzustellen, dass das Ergebnis eines Abfrageausdrucks niemals das Quellenobjekt selbst ist, da dies den Typ und die Identität der Quelle für den Client der Abfrage offen legen würde. Daher schützt dieser Schritt degenerierte Abfragen, die direkt im Quellcode geschrieben werden, indem explizit Select für die Quelle aufgerufen wird. Es liegt dann an den Implementierern von Select und anderen Abfrage-Operatoren, sicherzustellen, dass diese Methoden das Quellobjekt selbst nie zurückgeben.

also drei Übersetzungen (unabhängig von der Datenquelle)

// Query       // Translation 
from x in proxy     proxy.Where(x => x) 
where x 
select x 


from x in proxy     proxy.Select(x => x) 
select x    


from x in proxy     proxy.Where(x => x) 
where x        .Select(x => x * 2) 
select x * 2 
+0

Cool, wusste das nicht. –

+0

Danke. Ich dachte, es wäre so etwas, aber ich war mir nicht sicher, warum es genau ignoriert wurde. – mcrumley

7

Es kompiliert, da die LINQ-Abfragesyntax eine lexikalische Substitution ist. Der Compiler stellt sich

int query = from x in proxy 
      where x 
      select x; 

in

int query = proxy.Where(x => x);  // note it optimises the select away 

und nur dann wird es prüfen, ob die Methoden Where und Select existieren tatsächlich von der Art der proxy. Entsprechend muss in dem konkreten Beispiel, das Sie angegeben haben, Select nicht unbedingt vorhanden sein, damit dies kompiliert werden kann.

Wenn Sie so etwas wie dieses hatte:

int query = from x in proxy 
       select x.ToString(); 

dann wäre es in umziehen:

int query = proxy.Select(x => x.ToString()); 

und die Select Methode aufgerufen werden würde.

+0

Nun, der Punkt ist, dass Select überhaupt nicht überprüft wird. Es war der degenerierte Abfrageteil, der das OP verwirrte, vermute ich. –

+0

@ Jon: Erweitert. – Timwi

Verwandte Themen