Nachdem ich meine Liste von ID3Tags jetzt sortieren kann, fehlt noch das Filtern einer generischen Liste.
Die Typen System.Collections.Generic.List und System.Array stellen eine Reihe von Methoden zur Verfügung, die einen einzigen Parameter vom Typ Predicate<T> erwarten.
- Exists
- Find
- FindAll
- FindIndex
- FindLast
- FindLastIndex
- ForEach
- RemoveAll
- TrueForAll
Für meinen Filter will ich die FindAll Methode verwenden, die als Ergebnis eine List<T> liefert, welche nur die Elemente der Ursprungsliste enthält, die das übergebene Prädikat erfüllen.
Prädikate sind eines der neuen generischen Features, die seit Version 2.0 des .Net Frameworks zur Verfügung stehen. Der Delegat System.Predicate wird wie folgt definiert:
Public Delegate Function Predicate(Of T)(obj As T) As Boolean
Eine Funktion, die als Prädikat fungieren soll, muss also einen einzigen Parameter akzeptieren, der vom gleichen Typ sein muss, wie die Elemente der Liste, die mit Hilfe dieses Prädikats bearbeitet werden soll. Der Rückgabewert ist ein Booleanwert, der angibt, ob das untersuchte Element in die Ergebnismenge übernommen werden soll.
Um alle Instanzen von ID3Tags zu bekommen, deren Titel das Wort "Happy" enthält, könnte man einen Code in dieser Form verwenden:
string compareValue = "Happy";
List<ID3Tag> titleFilteredList = new List<ID3Tag>();
foreach (ID3Tag id3Tag in id3Tags)
{
if (id3Tag.Title.Contains(compareValue))
{
titleFilteredList.Add(id3Tag);
}
}
Alle Elemente der Ausgangsliste werden durchlaufen; wenn der Titel den gesuchten Wert enthält, wird das Element der Ergebnismenge angefügt. Achtung: in diesem Beispiel wird nur eine Referenz auf das Element in die Ergebnisliste angefügt, keine Kopie!
Die FindAll-Methode funktioniert vom Prinizip her genauso wie die Schleife im obigen Code. Das übergebene Prädikat entspricht der Überprüfung innerhalb der Schleife. Es bestimmt, was beim einzelnen Element geprüft werden soll. Zunächst muss also eine Funktion definiert werden, die ein Element vom Typ ID3Tag entgegennimmt und nach der Überprüfung des Elements einen booleschen Wert zurückgibt. Der Vergleichswert wird schon im Konstruktor der SearchFilter-Klasse übergeben.
internal class SearchFilter
{
// Vergleichswert
private string compareValue = "";
public SearchFilter(string compareValue)
{
this.compareValue = compareValue;
}
// Suche nach Teil von Titel
public bool TitleContains(ID3Tag id3Tag)
{
return id3Tag.Title.Contains(compareValue);
}
}
Jetzt kann das Prädikat erzeugt und der FindAll()-Methode übergeben werden. Das Prädikat ist - als Delegat - lediglich die Adresse einer aufrufbaren Methode.
//Vergleichswert für Suche nach Titel im Konstruktor übergeben
SearchFilter titleSearchFilter = new SearchFilter("Happy");
// Prädikat vorbereiten
Predicate<ID3Tag> filterByTitle =
new Predicate<ID3Tag>(titleSearchFilter.TitleContains);
// Liste mit gefiltertem Ergebnis
List<ID3Tag> titleFilteredList = id3Tags.FindAll(filterByTitle);
Die anderen Methoden, die ich oben erwähnt hatte und die ebenfalls ein Pedicate<T> als Parameter erwarten, funktionieren ähnlich. So iteriert Exists ebenfalls durch alle Elemente und gibt true zurück, wenn das Prädikat das erste mal für ein Element true zurückgibt. FindLastIndex durchläuft die Liste von hinten nach vorne und gibt den Index des Elements aus, bei dem das Prädikat das erste mal true zurückgibt, usw. Interessant sind auch die ForEach()- und RemoveAll()-Methoden.
Mit Prädikaten spart man sich den Code, um durch Elemente einer Liste zu iterieren. OK, vielleicht nicht so triftig, dieser Grund. Aber bezüglich Wiederverwendbarkeit und Austauschbarkeit zur Laufzeit haben Prädikate ihren Charme.