LINQ-Provider als D(o)I(t)Y(oursef)

by Bernhard Wurm 5. November 2010 11:35

LINQ funktioniert mit allen Objekten, welche IEnumerable implementieren. Richtig? Richtig!
Doch dies ist nicht unbedingt ein muss!

Verschiedene Linq-Provider ermöglichen den Zugriff auf verschiedenste Datenquellen. Z.B.: Linq2Sql, Linq2Xml, Linq2Sap, Linq2Twitter und so weiter. Auch selbst kann ein entsprechender Provider gebaut werden indem das Interface IQueryProvider implementiert wird. Doch wer einen entsprechenden Provider implementiert wird viele Methoden zu implementieren haben, damit die Linq-Syntax auch wirklich unterstützt wird und keine Laufzeitfehler auftreten. Es sind nicht nur die Methoden select und where zu implementieren sondern auch groupby etc. werden diese nicht implementiert, so kommt es zu einem Laufzeitfehler.

Wie kann für eine eigene Klasse eine einfache, eingeschränkte Linq-Abfrage abgesetzt werden ohne dass ein eigener großer Provider implementiert wird und gleichzeitig der Compiler Prüfungen durchführen kann.

Interessanter Punkt:

Was man mit LINQ abfragen will muss nicht IEnumerable<T> implementieren! Es muss lediglich eine GetEnumerator()-Methode verfügbar sein. Ja nicht mal für foreach muss IEnumerable implementiert werden. Auch hier reicht eine GetEnumerator()-Methode.

Spannendes zu LINQ:

Was muss ich machen, damit meine Klasse MyContainerClass mit Linq abfragen kann?

var data = new MyContainerClass();
var result = from d in data
where d.Name == "Third"
select d;

Dabei soll jedoch lediglich select und where verfügbar sein, da ich mich z.B.: über groupby nicht kümmern will. Es soll aber wie gesagt auch nicht zu einem Runtime-Fehler führen, sondern der Compiler prüfen.

Nun ja. Als erstes wird die Klasse mit der Eigenschaft Name, sowie der GetEnumerator-Methode.

class MyContainerClass  {
//Simulate data :)
static MyContainerClass[] data = new MyContainerClass[] {
new MyContainerClass() { Name = "First" },
new MyContainerClass() { Name = "Second" },
new MyContainerClass() { Name = "Third" },
new MyContainerClass() { Name = "Fourth" }
};

public string Name {get; set;}

public IEnumerator<MyContainerClass> GetEnumerator() {
foreach (var item in data)
yield return item;
}
}

So weit so einfach. Doch wie bekommen wir nun die echte LINQ Unterstützung? Ebenfalls ganz einfach: Man implementiere lediglich Methoden mit einem bestimmten Namen und einer bestimmten Signatur:

class MyContainerClass  {
  //Simulate data :)
  static MyContainerClass[] data = new MyContainerClass[] {
    new MyContainerClass() { Name = "First" },
    new MyContainerClass() { Name = "Second" },
    new MyContainerClass() { Name = "Third" },
    new MyContainerClass() { Name = "Fourth" }
  };
  public string Name {get; set;}
  public IEnumerator<MyContainerClass> GetEnumerator() {
    foreach (var item in data)
      yield return item;
  }
  public IEnumerable<MyContainerClass> Where(Func<MyContainerClass, bool> predicate) {
    return data.Where(predicate);  //Simulate where by just delegate it
  }
  public IEnumerable<MyContainerClass> Select(Func<MyContainerClass, MyContainerClass> selector) {
    return data.Select(selector);  //Simulate select by just delegate it
  }
}

FERTIG! Das ist es. Es müssen nur die Methoden vorhanden sein und der Compiler lässt die LINQ-Syntax zu.

Man lernt nie aus!

Categories: Programmiersprachen | C#