Das Indizieren von Indexern in C# erlaubt es uns, eigene Klassen und Strukturen mittels Indexern zu verwenden. Dies funktioniert in der Anwendung genauso wie der Zugriff auf einzelne Elemente in einem Array mithilfe des []-Operator (siehe Abb. 1).
1: int [] values = new int[] {45, 65, 104, 168 };
2: values[0] = 30;
3: Console.WriteLine(values[0]);
|
Abb. 1: Zugriff auf ein Array-Element über einen Indexer |
In einem Array erfolgt der Zugriff auf ein Element immer über einen Index vom Typ int, jedoch können auch andere Datentypen für Indexer verwendet werden. Dies wird deutlich, wenn man beispielsweise die Klasse Dictionary genauer betrachtet. Diese verwaltet Schlüssel-Wert-Paare, bei denen sowohl Schlüssel, als auch wert, generisch deklariert sind. Dadurch können auch Schlüssel vom Typ string genutzt werden, um mittels Indexern auf bestimmte Werte zuzugreifen (siehe Abb. 2).
1: Dictionary<string, int> values = new Dictionary<string, int>();
2: values.Add("id386", 20);
3: Console.WriteLine(values["id386"]);
|
Abb. 2: Auslesen eines Wertes in einem Dictionary "values" mithilfe eines Indexers vom Typ string |
Darüberhinaus können mehrere Indexer unterschiedlichen Typs indiziert werden, wobei man von Überladung spricht. Dies ähnelt stark der Überladung von Methoden. Das Indizieren von Indexern wird üblicherweise dann angewandt, wenn ein Objekt als Container für weitere Objekte dient und wir einen vereinfachten Zugriff auf Unterobjekte mit eindeutigen Identifizierern benötigen. Schauen wir uns zur Veranschaulichung das Indizieren und Überladen von Indexern einmal an einem Praxisbeispiel an.
Wir wollen eine Klasse KundenListe implementieren, welche es uns erlaubt, Instanzen vom Typ Kunde zu verwalten (siehe Abb. 3).
1: public class Kunde
2: {
3: public int ID { get; set; }
4:
5: public string Voranme { get; set; }
6:
7: public string Nachname { get; set; }
8:
9: public string Mail { get; set; }
10:
11: public override string ToString()
12: {
13: return $"ID: {this.ID}\nVorname: {this.Voranme}\nNachname: {this.Nachname}\nMail: {this.Mail}";
14: }
15: }
|
Abb. 3: Die Klasse Kunde, die von unserer Klasse KundenListe verwaltet werden soll |
Für eine eindeutige Identifikation eines Kunden in einer Auflistung stehen uns die Eigenschaften ID vom Typ int und Mail vom Typ string zur Verfügung. Für vereinfachte Zugriffe auf zuvor genannte Eigenschaften möchten wir in unserer Klasse KundenListe Indexer indizieren. Der Code hierfür wird in Abb. 4 dargestellt.
1: public class KundenListe : List<Kunde>
2: {
3: public new Kunde this[int kundenId]
4: {
5: get
6: {
7: foreach (Kunde k in this)
8: if (k.ID == kundenId) return k;
9: return null;
10: }
11: }
12:
13: public Kunde this[string kundenMail]
14: {
15: get
16: {
17: foreach (Kunde k in this)
18: if (k.Mail == kundenMail) return k;
19: return null;
20: }
21: }
22: }
|
Abb. 4: Indizierung von Indexern für die Eigenschaften ID und Mail für einen vereinfachten Zugriff auf die KundenListe |
Für das Indizieren von Indexern verwenden wir gewöhnliche .NET-Properties, gefolgt von this in der Deklaration, um anzugeben, dass wir uns auf die Klasse KundenListe beziehen. Darauf folgt der []-Operator, welcher zur Kennzeichnung der Indizierung sowie Klammerung der Übergabeparameter dient. Somit wird in Abb. 4, Zeile 3, ein Indexer für KundenListe definiert, welcher als Übergabeparameter eine Kunden-ID vom Typ int erwartet. Mithilfe dieses Parameters wird anschließend in der KundenListe nach der entsprechenden Instanz gesucht, welche wiederrum bei erfolgreicher Suche an den Aufrufer zurückgeliefert wird. Hierbei nutzen wir zusätzlich noch das Schlüsselwort new, um den bereits indizierten Indexer aus der Basisklasse List zu verdecken. Die Schlüsselwörter virtual, sealed, override und abstract sind ebenfalls zulässig. Als static dürfen diese .NET-Properties jedoch nicht deklariert werden.
Für die Überladung des Indexers, mit dessen Hilfe wir über die Mail-Adresse auf eine bestimmte Kunden-Instanz zugreifen wollen, gehen wir nach dem gleichen Schema vor. Nur diesmal definieren wir anstatt eines Parameters vom Typ int, einen Parameter vom Typ string, an den der Aufrufer die Mail-Adresse übergeben kann (siehe Abb. 5, Zeile 13). Anschließend suchen wir in unserer Liste nach dem entsprechenden Eintrag und geben diesen zurück. Die Verwendung der Klasse KundenListe sowie der indizierten und überladenen Indexer wird in Abb. 6 veranschaulicht.
1: KundenListe kundenListe = new KundenListe();
2: kundenListe.Add(k1);
3: kundenListe.Add(k2);
4: kundenListe.Add(k3);
5:
6: // Zugriff mittels ID vom Typ int
7: Console.WriteLine(kundenListe[44565]);
8: // Zugriff über E-Mail vom Typ string
9: Console.WriteLine(kundenListe[jakob@mail.com]);
|
Abb. 6: Verwendung der KundenListe mit Zugriff über indizierte Indexer |
Viel Spaß beim Indizieren und Überladen!