2009-09-17 1 views
5

Wichtige Die Frage ist nicht: „Was tut Queryable.OfType tun, es ist‚ wie der Code ich es erreichen, dass siehe‘Wie funktioniert Queryable.OfType?

Nachdenken über Queryable.OfType sehe ich (nach einiger Bereinigung?):

public static IQueryable<TResult> OfType<TResult>(this IQueryable source) 
    { 
     return (IQueryable<TResult>)source.Provider.CreateQuery(
      Expression.Call(
       null, 
       ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(
        new Type[] { typeof(TResult) }) , 
        new Expression[] { source.Expression })); 
    } 

lassen Sie mich sehen, ob ich diese gerade haben.

  1. Verwenden Reflexion einen Verweis auf die aktuelle Methode (OfType) greifen
  2. Erstellen Sie eine neue Methode, die genau die gleiche ist, indem Sie MakeGenericMethod verwenden, um den Typparameter der aktuellen Methode in genau die gleiche Sache zu ändern.
  3. Das Argument für diese neue Methode wird nicht Quelle sondern source.Expression sein. Das ist kein IQueryable, aber wir werden das Ganze an Expression.Call übergeben, also ist das in Ordnung.
  4. Anruf Expression.Call, vorbei null als Methode (seltsam?)Beispiel und das klonierte Verfahren als Argumente.
  5. Übergeben Sie das Ergebnis an CreateQuery und geben Sie das Ergebnis aus, das wie der sinnvollste Teil des Ganzen aussieht.

Nun ist die Wirkung dieses Verfahrens ist ein Ausdruck zurückzukehren, die der Betreiber teilt alle Werte wegzulassen zurückkehrt, wo der Typ nicht gleich TResult oder einer seiner Subtypen ist. Aber ich kann nicht sehen, wie die obigen Schritte das tatsächlich erreichen. Es scheint, einen Ausdruck zu erstellen, der eine Methode darstellt, die IQueryable <TResult> zurückgibt und den Rumpf dieser Methode einfach den gesamten Quellausdruck macht, ohne jemals den Typ zu betrachten. Ist es einfach zu erwarten, dass ein IQueryable-Provider einfach keine Datensätze zurückgibt, die nicht vom ausgewählten Typ sind?

Sind also die obigen Schritte in irgendeiner Weise falsch, oder sehe ich nur nicht, wie sie das Verhalten zur Laufzeit ergeben?

Antwort

5

Es wird nicht in null als die Methode übergeben - es als "Zielausdruck" übergeben, d. H. Was es die Methode aufruft. Dies ist null, da OfType eine statische Methode ist, also kein Ziel benötigt wird.

Der Punkt MakeGenericMethod Aufruf ist, dass GetCurrentMethod() die offene Version zurückgibt, das heißt OfType<> statt OfType<YourType>.

Queryable.OfType selbst ist nicht bedeutet, um irgendwelche der Logik für das Auslassen der Rückgabe irgendwelcher Werte enthalten. Das hängt vom LINQ-Anbieter ab. Der Punkt Queryable.OfType besteht darin, den Ausdrucksbaum aufzubauen, um den Aufruf an OfType einzuschließen, so dass, wenn der LINQ-Provider ihn schließlich in sein natives Format (z. B. SQL) konvertieren muss, OfType aufgerufen wurde.

So funktioniert Queryable funktioniert im Allgemeinen - im Grunde lässt es den Anbieter den gesamten Abfrageausdruck als Ausdrucksbaum sehen. Das ist alles, was es tun soll - wenn der Anbieter aufgefordert wird, dies in echten Code zu übersetzen, das ist, wo die Magie passiert.

Queryable könnte unmöglich die Arbeit selbst tun - es hat keine Ahnung, welche Art von Datenspeicher der Anbieter darstellt. Wie konnte es mit der Semantik von OfType kommen, ohne zu wissen, ob der Datenspeicher SQL, LDAP oder etwas anderes war? Ich stimme zu, es dauert eine Weile, um Ihren Kopf zu bekommen, aber :)

+0

OK, habe ich das gerade? 1. Der Aufruf von MakeGenericMethod ist erforderlich, da Sie eine geöffnete OfType-Version mit einem Argument von YourType (separat) übergeben können, aber OfType nicht als ein Argument übergeben können. Dieser Teil macht zumindest Sinn. 2. Der Sinn von all dem ist, dem Provider einfach "go import OfType" zu sagen, anstatt einen Ausdruck einer Implementierung von OfType zu erzeugen. Fortsetzung im nächsten Kommentar ... –

+0

Es kann jedoch nicht einfach etwas in der Art von Expression.OfType zurückgegeben werden (was ich aufgrund Ihrer Erklärung erwarten würde), weil das nicht wirklich existiert. Liefern sie stattdessen etwas, was dasselbe ist, wird nur aus irgendeinem Grund anders umgesetzt? –

+1

Nein, es gibt etwas zurück, das einen Aufruf von 'Queryable.OfType' darstellt, genau wie' Select' ein 'IQueryable ' zurückgibt, das die vorherige Abfrage mit einem angeketteten Aufruf von 'Queryable.Select' repräsentiert. Es ist kein * Ausdruck * von 'OfType', es ist ein Ausdruck, der einen * Aufruf * zu' OfType' darstellt. –