Vorspann: Wieso eine Klasse erstellen die
IEnumerable<T> implementiert ??
Vor kurzen wurden die
Reactive Extensions(Rx) für das .NET-Framework releast und diese sind ein wenig komplex :) In diesem Post soll es auch weniger um Reactive Extensions gehen als viel mehr um die Implementation von IEnumerable<T>. Diese Schnittstelle einmal selber zu implementieren und natürlich auch zu verstehen wie es funktioniert ist ein guter Anfang um sich mit der Arbeits- und Funktionsweise von Rx vertraut zu machen.
Wofür dient IEnumerable<T>? Implementiert eine Klasse diese Schnittstelle ist es möglich mit einer foreach-Schleife über sie zu iterieren. Des weiteren kann man außerdem mit LINQ, Abfragen auf die Klasse schreiben. Dies gilt allerdings nur für die generische Schnittstelle, da diese Typsicherheit bietet!
Meine Klasse, die IEnumerable<T> implementiert, ist in dem Beispiel die Klasse
GenericData mit einem Property
elements:
public class GenericData<TSource> : IEnumerable<TSource>
{
private TSource[] elements;
Implementiert man also IEnumerable<T> muss man folgende Methoden mit Code füllen.
public IEnumerator<TSource> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
Um diese Beiden Methoden zu implementieren, benötigen wir eine innere Klasse, die
IEnumerator<T> implemetiert. Ich habe dies folgendermaßen getan.
private class DataEnumerator : IEnumerator<TSource>
{
public GenericData<TSource> internalData;
public int index = -1;
public DataEnumerator(GenericData<TSource> data)
{
internalData = data;
}
public TSource Current
{
get
{
return internalData.elements[index];
}
}
public void Dispose()
{
this.Reset();
}
object IEnumerator.Current
{
get { return this.Current; }
}
public bool MoveNext()
{
index++:
if (index < internalData.elements.Length - 1)
{
return true;
}
else
{
return false;
}
}
public void Reset()
{
this.index = -1;
}
}
Ist dies getan können wir unsere beiden zuvor implementierten Methoden wie folgt mit Code füllen
public IEnumerator<TSource> GetEnumerator()
{
return new DataEnumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
Ist dies erledigt könne wir über eine Instanz unserer Klasse mit einer foreach-Schleife iterieren uns sogar LINQ auf sie anwenden, aber dies ist noch nicht der springende Punkt auf den ich hinaus möchte. Das alles war jetzt schon ein wenig viel Code im Vergleich dessen wie leicht und schnell man es sich eigentlich machen kann!!
Mit dem Schlüsselwort yield ist es möglich auf die innere Klasse zu verziechten und benötigt lediglich 2 Zeilen Code um das selbe Ergebnis zu erzielen:
public class GenericData<TSource> : IEnumerable<TSource>
{
private TSource[] elements;
private int elementsCount = 0;
public void Add(TSource item)[...]
public IEnumerator<TSource> GetEnumerator()
{
foreach (TSource element in elements)
{
yield return element;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
So einfach ist es! Wenn man nun mit einer foreach-Schleife über eine Instanz dieser Klasse iteriert landet man in der Schleife in der GetEnumerator-Methode. In dieser wird in jedem Schleifen-Schritt nun mit dem Schlüsselwort yield ein Element zurückgegeben für welches dann der Code in der ersten foreach-Schleife ausgeführt wird.
Dies ist ein guter Einstieg bzw. es ist ein guter Rat dies nachzuvollziehen um sich mit den komplexen
Reactive Extensions auseinander zu setzen. Zu diesen werde ich vielleicht auch nochmal ein Wort hier verlieren, wenn ich schon eine mehr oder weniger Minieinleitung gebe :D