Gleichartige Daten können komfortabel in Auflistungen gespeichert werden. In diesem Kapitel werden passende Schnittstellen und Klassen ebenso vorgestellt, wie die Abfragesprache LINQ.
6 Collections und LINQ
Im Namensraum System.Collections.Generic sind Klassen, die, ähnlich den in Abschnitt 2.11, »Datenfelder (Arrays)«, beschriebenen Arrays, Sammlungen von Objekten speichern. Anders als bei Arrays erfolgt der Zugriff nicht nur über einen ganzzahligen Index, sondern die Elemente werden auch direkt identifiziert. Außerdem ist die Größe der Sammlung nicht fest, sondern passt sich automatisch an. So wird das Einfügen und Löschen von Elementen zum Kinderspiel.
Hinweis |
Ich habe hier wegen ihrer Typsicherheit die generischen Varianten gewählt. Die meisten Darstellungen gelten auch für die Variante ohne Of-Klausel. |
Hinweis |
Durch die Möglichkeiten, Klassen zu erweitern, können Collections noch viel mehr als hier beschrieben (siehe Abschnitt 4.8.4, »Klassenerweiterungen: Extension«). |
6.1 Sammlungsschnittstellen 

Die Sammlungen basieren auf Schnittstellen, die die Verwaltung der Elemente größtenteils regeln. Auf weitergehende Funktionalität der implementierenden Klassen gehen wir in Abschnitt 6.2, »Sammlungsklassen«, ein. Bitte beachten Sie, dass dieselbe Schnittstelle durch Mehrfachvererbung mehr als einmal in dem folgenden Ausschnitt aus der Vererbungshierarchie auftauchen kann. Bei Klassen ist dies wegen der Einfachvererbung nicht möglich.
1: System
2: System.AddIn.Contract
3: System.Collections
4: System.Collections.Generic
5: System.Linq
6: System.ServiceModel
7: System.ServiceModel.Dispatcher
3-+IEnumerable
| +3-ICollection
| +4--------IEnumerable(Of T)
| | +4--ICollection(Of T)
| | | +4-+IDictionary(Of TKey,TValue)
| | | | | +7--IMessageFilterTable(Of TFilterData)
| | | | +IList(Of T)
| | | +6--IExtensionCollection
| | | (Of T As {IExtensibleObject(Of T)})})
| | +5-+IGrouping(Of TKey,TElement)
| | +ILookup(Of TKey,TElement)
| | +IOrderedEnumerable(Of TElement)
| +5--IQueryable |
| +5-+------+----------IQueryable(Of T)
| | |
| +IOrderedQueryable |
| +----------------+-5--IOrderedQueryable(Of T)
+IEnumerator
| 1--IDisposable
| +2--IEnumeratorContract(Of T)
+------+4--IEnumerator(Of T)
6.1.1 IEnumerable 

Die Schnittstelle IEnumerable(Of T) ist uns schon implizit bei der For Each-Schleife begegnet, denn Typen, die diese Schnittstelle implementieren, können mit dieser Schleife durchlaufen werden. Ihre einzige Funktion GetEnumerator() As IEnumerator(Of T) liefert das Objekt, das zum Durchlaufen verwendet wird (siehe Tabelle 6.1).
Mitglied | Beschreibung |
MoveNext() As Boolean |
Fortschritt zum nächsten Element und Rückgabe, ob weitere Elemente folgen |
Current As T |
Auslesen des aktuellen Elements |
Reset() |
Zurücksetzen des Elementzeigers |
Damit lässt sich die For Each-Schleife »nachprogrammieren«:
Dim text As String = "Sammlung"
Dim en As IEnumerator(Of Char) = text.GetEnumerator()
While en.MoveNext()
Console.Write(en.Current & " ")
End While
Die Ausgabe zeigt, dass die Buchstabensammlung (der Text) durchlaufen wurde:
S a m m l u n g
6.1.2 ICollection 

Die Schnittstelle IEnumerable(Of T) ist zwar sehr einfach, hat aber den Nachteil, dass nur eine bereits vorhandene Sammlung ausgelesen, nicht aber eine neue aufgebaut werden kann. Dies ist mit der Schnittstelle ICollection(Of T) möglich (siehe Tabelle 6.2).
Mitglied | Beschreibung |
Add(item As T) |
Neues Element hinzufügen |
Clear() |
Sammlung leeren |
Contains(item As T) As Boolean |
Gibt an, ob ein Element bereits enthalten ist. |
CopyTo(array As T(), index As Integer) |
Kopie der Sammlung ein in Array, angefangen bei index |
Remove(item As T) As Boolean |
Entfernt ein Element und gibt eine Erfolgsmeldung aus. |
Count As Integer |
Elementanzahl in der Sammlung auslesen |
IsReadOnly As Boolean |
Auslesen, ob die Sammlung schreibgeschützt ist |
Damit kann bereits sinnvoll gearbeitet werden. Das folgende Codefragment nutzt ein paar der in der Schnittstelle definierten Mitglieder.
'...\Sammlungen\Schnittstellen\ICollection.vb |
Option Explicit On
Imports System.Collections.ObjectModel
Namespace Sammlungen
Module ICollection
Sub Test()
Dim text As ICollection(Of Char) = New Collection(Of Char)()
For Each ch As Char In "superkalifragilistischexpialigetisch"
text.Add(ch)
Next
Console.WriteLine("Das Wort hat {0} Buchstaben.", text.Count)
text.Remove("i"c)
Console.WriteLine("Da sind noch ""i"": {0}.", text.Contains("i"c))
For Each ch As Char In text : Console.Write(ch) : Next
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe zeigt, dass Remove genau ein Element entfernt und nicht alle.
Das Wort hat 36 Buchstaben.
Da sind noch "i": True.
superkalfragilistischexpialigetisch
Die Lückenlosigkeit der letzten Zeile macht noch auf etwas sehr Wichtiges aufmerksam:
Sammlungen sind lückenlos: Beim Einfügen rutschen Elemente nach hinten, beim Löschen rücken welche nach. |
Diese Schnittstelle schlägt mit der Methode CopyTo eine Brücke zu den Arrays. Dies ist wichtig, weil es viele Methoden gibt, die nur ein Array akzeptieren. Die beiden folgenden Zeilen zeigen exemplarisch einen Aufruf:
Dim c(35) As Char
text.CopyTo(c, 0)
6.1.3 IDictionary 

Wörterbücher beliebiger Art basieren auf der Schnittstelle IDictionary(Of TKey, TValue) (siehe Tabelle 6.3). Da im Gegensatz zu den bisherigen Sammlungsschnittstellen kein einfacher Wert im Spiel ist, sondern ein Schlüssel-Wert-Paar, müssen die Funktionen zum Einfügen und Löschen von Elementen neu festgelegt werden. Insbesondere die von ICollection geerbte Add-Methode ist hier noch nicht spezifiziert und wird erst durch die Implements-Klausel der implementierenden Klasse festgelegt.
Mitglied | Beschreibung |
ContainsKey(key As TKey) As Boolean |
Gibt an, ob ein Schlüssel Teil der Sammlung ist. |
Add(key As TKey, value As TValue) |
Neues Element hinzufügen |
Remove(key As TKey) As Boolean |
Entfernt das zum Schlüssel gehörende Element und gibt eine Erfolgsmeldung. |
TryGetValue(key As TKey, ByRef value As TValue) As Boolean |
Speichert den zum Schlüssel gehörenden Wert und gibt eine Erfolgsmeldung. |
Item(key As TKey) As TValue |
Indexer: Sammlung(Schlüssel) liest Wert. |
Keys() As ICollection(Of TKey) |
Alle Schlüssel in der Reihenfolge von Values |
Values() As ICollection(Of TValue) |
Gespeicherte Werte in der Reihenfolge von Keys |
Das folgende Codefragment verwendet einige der Mitglieder von IDictionary:
'...\Sammmlungen\Schnittstellen\IDictionary.vb |
Option Explicit On
Namespace Sammlungen
Module IDictionary
Sub Test()
Dim Größe As IDictionary(Of String, Integer) = _
New Dictionary(Of String, Integer)()
Größe.Add("Napoleon", 168)
Größe.Add("Merkel", 164)
Größe.Add("Kohl", 193)
Console.WriteLine("Arzt entfernt: {0}", Größe.Remove("Schweitzer"))
For Each name As String In Größe.Keys
Console.WriteLine("{0} ist/war {1} cm groß", name, Größe(name))
Next
Console.WriteLine("Die Größe von Bush ist {0}bekannt", _
If(Größe.ContainsKey("Bush"), "", "un"))
Dim gr As Integer
Größe.TryGetValue("Bush", gr)
Console.WriteLine("Wert für unbekannte Größe ist {0}", gr)
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe zeigt, dass Länge und Größe nicht immer korrelieren:
Arzt entfernt: False
Napoleon ist/war 168 cm groß
Merkel ist/war 164 cm groß
Kohl ist/war 193 cm groß
Die Größe von Bush ist unbekannt
Wert für unbekannte Größe ist 0
6.1.4 IList 

Die Schnittstelle IList(of T) ergänzt ICollection um einen indexbasierten Zugriff (siehe Tabelle 6.4). Damit ist eine Verwendung ähnlich einem Array möglich, wenn auch mit erheblich höherem Komfort (die Liste wächst automatisch, objektbasierter Zugriff ist außerdem möglich).
Mitglied | Beschreibung |
IndexOf(item As T) As Integer |
Position eines Elements |
Insert(index As Integer, item As T) |
Neues Element an einer bestimmten Stelle einfügen |
RemoveAt(index As Integer) |
Element an einer bestimmten Stelle löschen |
Item(index As Integer) As T |
Indexer: Sammlung(Index) gibt den Wert aus. |
Da die Add-Methode von ICollection(Of T) geerbt wird und diese Schnittstelle nichts von Indizes weiß, kann auch keine allgemeingültige Aussage über die Position eingefügter Elemente gemacht werden. In vielen Fällen wird das Element angehängt. Wenn Sie jedoch sicher sein wollen, ohne die Dokumentation lesen zu müssen, verwenden Sie am besten Insert. Dabei sollten Sie beachten, dass eine nicht existierende Position eine ArgumenrOutOfRangeException auslöst. Das folgende Codefragment nutzt das Einfügen an einer gegebenen Position:
'...\Sammlungen\Schnittstellen\IList.vb |
Option Explicit On
Namespace Sammlungen
Module IList
Sub Test()
Dim Warteschlange As IList(Of String) = New List(Of String)()
Warteschlange.Insert(0, "Müller")
Warteschlange.Insert(1, "Maier")
Warteschlange.Insert(1, "Schulze")
For Each name As String In Warteschlange
Console.Write("{0} ", name)
Next
Console.WriteLine()
Console.WriteLine("Maier ist auf Position {0}", _
Warteschlange.IndexOf("Maier"))
Console.WriteLine("An zweiter Stelle steht {0}", Warteschlange(1))
Warteschlange(1) = "Schmidt"
Console.WriteLine("An zweiter Stelle steht {0}", Warteschlange(1))
Try
Warteschlange.Insert(4, "Springer")
Catch ex As Exception
Console.WriteLine("Ausnahme {0}", ex.Message)
End Try
Console.ReadLine()
End Sub
End Module
End Namespace
Die Ausgabe zeigt, wie der eingefügte Name den anderen nach hinten schiebt. Ein Elementersatz ist durch einfache Zuweisung wie im Fall Schmidt möglich. Außerdem sehen Sie, dass die Positionsangabe in Insert höchstens so groß sein darf, wie die Liste lang ist, da sonst undefinierte Lücken entstehen würden.
Müller Schulze Maier
Maier ist auf Position 2
An zweiter Stelle steht Schulze
An zweiter Stelle steht Schmidt
Ausnahme Index must be within the bounds of the List.
Parameter name: index
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.