Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 7 Weitere Möglichkeiten von C#
Pfeil 7.1 Namensräume (Namespaces)
Pfeil 7.1.1 Zugriff auf Namespaces
Pfeil 7.1.2 Die »using«-Direktive
Pfeil 7.1.3 Globaler Namespace
Pfeil 7.1.4 Vermeiden von Mehrdeutigkeiten
Pfeil 7.1.5 Namespaces festlegen
Pfeil 7.1.6 Der ::-Operator
Pfeil 7.2 Generics – generische Datentypen
Pfeil 7.2.1 Lösung mit einer generischen Klasse
Pfeil 7.2.2 Typparameter mit Constraints einschränken
Pfeil 7.2.3 Generische Methoden
Pfeil 7.2.4 Generics und Vererbung
Pfeil 7.2.5 Konvertierung von Generics
Pfeil 7.2.6 Generische Delegates
Pfeil 7.2.7 Generische Klassen in der .NET-Klassenbibliothek
Pfeil 7.3 Operatorüberladung
Pfeil 7.3.1 Syntax der Operatorüberladung
Pfeil 7.3.2 Operatorüberladungen in »GeometricObjectsSolution«
Pfeil 7.3.3 »true«- und »false«-Operatoren
Pfeil 7.3.4 Benutzerdefinierte Konvertierungen
Pfeil 7.4 Indexer
Pfeil 7.4.1 Überladen von Indexern
Pfeil 7.4.2 Parameterbehaftete Eigenschaften
Pfeil 7.5 Erweiterungsmethoden
Pfeil 7.6 Implizit typisierte Variablen (Typinferenz)
Pfeil 7.7 Lambda-Ausdrücke
Pfeil 7.8 Anonyme Typen
Pfeil 7.9 Nullable-Typen
Pfeil 7.10 Attribute
Pfeil 7.10.1 Das »Flags«-Attribut
Pfeil 7.10.2 Anmerkungen zu den Attributen
Pfeil 7.10.3 Benutzerdefinierte Attribute
Pfeil 7.10.4 Attribute auswerten
Pfeil 7.10.5 Festlegen der Assembly-Eigenschaften
Pfeil 7.11 Partielle Methoden
Pfeil 7.12 Dynamisches Binden
Pfeil 7.12.1 Eine kurze Analyse
Pfeil 7.12.2 Dynamische Objekte
Pfeil 7.13 Unsicherer (unsafe) Programmcode – Zeigertechnik in C#
Pfeil 7.13.1 Einführung
Pfeil 7.13.2 Das Schlüsselwort »unsafe«
Pfeil 7.13.3 Deklaration von Zeigern
Pfeil 7.13.4 Die »fixed«-Anweisung
Pfeil 7.13.5 Zeigerarithmetik
Pfeil 7.13.6 Der Operator »->«


Galileo Computing - Zum Seitenanfang

7.4 Indexer Zur nächsten ÜberschriftZur vorigen Überschrift

In Kapitel 2, »Grundlagen der Sprache C#«, haben Sie gelernt, mit Arrays zu arbeiten. Sie wissen, wie Sie ein Array deklarieren und auf die einzelnen Elemente zugreifen können, zum Beispiel:


int[] intArr = new int[10];
intArr[3] = 125;

Mit C# können Sie Klassen und Strukturen so definieren, dass deren Objekte wie ein Array indiziert werden können. Indizierbare Objekte sind in der Regel Objekte, die als Container für andere Objekte dienen – vergleichbar einem Array. Das .NET Framework stellt uns eine ganze Reihe solcher Klassen zur Verfügung, die als Collections oder Auflistungen bezeichnet werden. Diese agieren ähnlich den uns schon bekannten Arrays, verwalten also Objekte.

Stellen Sie sich vor, Sie würden die Klasse Fußballmannschaft entwickeln. Eine Mannschaft setzt sich aus vielen Einzelspielern zusammen, die innerhalb der Klasse in einem Array vom Typ Spieler verwaltet werden. Wenn Sie die Klasse Fußballmannschaft mit


Fußballmannschaft Wacker = new Fußballmannschaft();

instanziieren, wäre es doch zweckdienlich, sich von einem bestimmten Spieler mit der Anweisung


string name = Wacker[2].Zuname;

den Zunamen zu besorgen. Genau das leistet ein Indexer. Wir übergeben dem Objekt einen Index in eckigen Klammern, der ausgewertet wird und die Referenz auf ein Spieler-Objekt zurückliefert. Darauf können wir mit dem Punktoperator den Zunamen des gewünschten Spielers ermitteln, vorausgesetzt, diese Eigenschaft ist in der Klasse Spieler implementiert.

Ein Indexer ist prinzipiell eine Eigenschaft, die mit this bezeichnet wird und in eckigen Klammern den Typ des Index definiert. Weil sich this immer auf ein konkretes Objekt bezieht, können Indexer niemals als static deklariert werden. Die Definition lautet:


<Modifikatoren> <Datentyp> this[<Parameterliste>] 

Als Modifizierer sind neben den Zugriffsmodifikatoren auch new, virtual, sealed, override und abstract zulässig. Wenn Sie sich in Erinnerung rufen, was Sie im vorhergehenden Abschnitt über Operatorüberladung gelernt haben, wird klar, dass Indexer eine Überladung des []-Operators sind.

Wenn eine Klasse einen Indexer definiert, darf diese Klasse keine Item-Methode haben, weil interessanterweise ein Indexer als Item-Methode interpretiert wird.

Mit diesem Wissen ausgestattet, sollten wir uns nun die Implementierung der Klasse Fußballmannschaft ansehen.


// --------------------------------------------------------------
// Beispiel: ...\Kapitel 7\Indexer
// --------------------------------------------------------------
class Program {
  static void Main(string[] args) {
    Fußballmannschaft Wacker = new Fußballmannschaft();
    // Spieler im Team aufnehmen
    Wacker[0] = new Spieler("Fischer", 23);
    Wacker[1] = new Spieler("Müller", 19);
    Wacker[2] = new Spieler("Mamic", 33);
    Wacker[1] = new Spieler("Meier", 31); 
    // Spielerliste ausgeben
    for (int index = 0; index < 25; index++) {
      if (Wacker[index] != null)
        Console.WriteLine("Name: {0,-10}Alter: {1}", 
                           Wacker[index].Zuname, Wacker[index].Alter);
    }
    Console.ReadLine();
  }
}
// Fussballmannschaft
public class Fußballmannschaft {
  private Spieler[] team = new Spieler[25];
  // Indexer
  public Spieler this[int index] {
    get { return team[index]; }
    set {
      // prüfen, ob der Index schon belegt ist
      if (team[index] == null)
        team[index] = value;
      else
        // nächsten freien Index suchen
        for (int i = 0; i < 25; i++) {
          if (team[i] == null) {
            team[i] = value;
            return;
          }
        } 
    }
  }
}
// Spieler
public class Spieler {
  public string Zuname;
  public int Alter;
  public Spieler(string zuname, int alter) {
    Zuname = zuname;
    Alter = alter;
  }
}

Jede Instanz der Klasse Fußballmannschaft verhält sich wie ein Array. Dafür verantwortlich ist der Indexer, der über das Schlüsselwort this deklariert wird und einen Integer entgegennimmt. Der Indexer ist vom Typ Spieler. Der lesende und schreibende Zugriff auf ein Element erfolgt unter Angabe seines Index, also beispielsweise:


Wacker[6];

Die interne Struktur eines Indexers gleicht der einer Eigenschaftsmethode: Sie enthält einen get- und einen set-Accessor. get wird aufgerufen, wenn durch die Übergabe des int-Parameters Letzterer als Index der Spieler-Arrays ausgewertet wird und den entsprechenden Spieler aus dem privaten Array zurückgibt. Die Zuweisung eines weiteren Spielers hat den Aufruf des set-Zweiges zur Folge. Dabei wird überprüft, ob der angegebene Index noch frei oder bereits belegt ist. Im letzteren Fall wird der erste freie Index gesucht.


Galileo Computing - Zum Seitenanfang

7.4.1 Überladen von Indexern Zur nächsten ÜberschriftZur vorigen Überschrift

In einem herkömmlichen Array erfolgt der Zugriff auf ein Element grundsätzlich über den Index vom Typ int, aber Indexer lassen auch andere Datentypen zu. In vielen Situationen ist es sinnvoll, anstelle des Index eine Zeichenfolge anzugeben, mit der ein Element identifiziert wird. Meistens handelt es sich dabei um den Namen des Elements. Sind mehrere unterschiedliche Zugriffe wünschenswert, können Indexer nach den bekannten Regeln hinsichtlich Anzahl und Typ der Parameter überladen werden.

Das folgende Beispiel zeigt eine Indexerüberladung. Dazu benutzen wir das Beispiel aus dem vorherigen Abschnitt und ergänzen die Klasse Fußballmannschaft um einen weiteren Indexer in der Weise, dass wir auch über dem Namen des Spielers auf das zugehörige Objekt zugreifen können, also zum Beispiel mit


Spieler spieler = Wacker["Fischer"];

Angemerkt sei dabei, dass das Beispiel nur so lange wunschgemäß funktioniert, solange die Namen eindeutig sind. Sollten mehrere Spieler gleichen Namens in der Liste zu finden sein, müssten weitere Kriterien zur eindeutigen Objektbestimmung herangezogen werden. Das soll aber an dieser Stelle nicht das Thema sein.


// --------------------------------------------------------
// Beispiel: ...\Kapitel 7\IndexerÜberladung
// --------------------------------------------------------
class Program {
  static void Main(string[] args) {
    Fußballmannschaft Wacker = new Fußballmannschaft();
    // Spieler im Team aufnehmen
    Wacker[0] = new Spieler("Fischer", 23);
    Wacker[1] = new Spieler("Müller", 19);
    Wacker[2] = new Spieler("Mamic", 33);
    Wacker[1] = new Spieler("Meier", 31);
    Console.Write("Spieler suchen: ... ");
    string spieler = Console.ReadLine();
    if (Wacker[spieler] != null)
      Console.WriteLine(Wacker[spieler].Alter);
    else
      Console.WriteLine("Der Spieler gehört nicht zum Team.");
    Console.ReadLine();
  }
}
// Fußballmannschaft
public class Fußballmannschaft {
  private Spieler[] team = new Spieler[25];
  // Indexer
  public Spieler this[int index] {
    ...
  }
  public Spieler this[string name] {
    get {
      for (int index = 0; index < 25; index++) {
        if (team[index] != null &&  team[index].Zuname == name)
          return team[index];
      }
      return null;
    }
  }
}

Die Überladung des Indexers mit einem string enthält nur den get-Accessor, da die Zuweisung eines neuen Spieler-Objekts nur anhand seines Namens in diesem Beispiel unsinnig wäre. Im get-Accessor wird eine Schleife über alle Indizes durchlaufen. Jeder Index wird dahingehend geprüft, ob er einen von null abweichenden Inhalt hat. Ist der Inhalt nicht null und verbirgt sich hinter dem Index auch das Spieler-Objekt mit dem gesuchten Namen, wird das Objekt an den Aufrufer zurückgegeben. Diese Operation wird durch


if (team[index] != null &&  team[index].Zuname == name)
  return team[index];

beschrieben. Sollte sich ein Spieler mit dem gesuchten Namen nicht in der Mannschaft befinden, ist der Rückgabewert null.


Galileo Computing - Zum Seitenanfang

7.4.2 Parameterbehaftete Eigenschaften topZur vorigen Überschrift

Eigenschaften sind von Haus aus parameterlos. Mit anderen Worten: Sie können einen Eigenschaftswert nicht in Abhängigkeit von einer oder mehreren Nebenbedingungen setzen. Dieses Manko lässt sich mit Indexern beheben, sodass beispielsweise mit


myObject.TheProperty[2] = 10;

der Eigenschaftswert festgelegt werden kann.

Parametrisierte Eigenschaften sind von Bedeutung, wenn Randbedingungen den von der Eigenschaft beschriebenen Wert beeinflussen. In der fiktiven Eigenschaft TheProperty lautet die Randbedingung »2«. Unter dieser Prämisse soll der Eigenschaft die Zahl 10 zugewiesen werden. Der Code ähnelt ohne Zweifel einem Array und lässt sich auch so interpretieren: Es handelt sich um eine indizierte Sammlung gleichnamiger Eigenschaftselemente.

Wir wollen uns jetzt ansehen, wie in einer Klasse eine parameterbehaftete Eigenschaft realisiert werden kann. Dazu stellen wir uns eine Klasse Car mit einer Eigenschaft Color vor. Ein Car-Objekt beschreibt das Auto eines beliebigen Herstellers. Wir wissen alle, dass die verschiedenen Autoproduzenten unterschiedliche Farbpaletten anbieten – oft modellabhängig. Einen Ferrari gibt es möglicherweise nur in Rot, Gelb oder Schwarz; kaufen Sie einen häufiger vertretenen Typ, können Sie vielleicht unter 20 verschiedenen Farben auswählen. Diese Situation soll die Eigenschaft Color der Car-Klasse beschreiben.

Der Eigenschaft Color wollen wir als Argument eine Zeichenfolge übergeben, die den Hersteller beschreibt. Zurückgeliefert wird daraufhin die Palette der zur Auswahl stehenden Farben. Ein Aufruf könnte dann wie folgt aussehen:


int[] colors = myCar.Color["Mazda"];

Das sei unser Ziel. Widmen wir uns nun dem Code. Der Teilausdruck


Color["Mazda"]

lässt sich über einen Indexer realisieren. Ein Indexer setzt ein Objekt voraus, denn wie wir wissen, überladen wir den »[]«-Operator in this, dem aktuellen Objekt also. Daraus kann gefolgert werden, dass wir zusätzlich zur Klasse Car eine zweite Klasse definieren müssen, die ihrerseits die Eigenschaft beschreibt. Im Folgenden soll der Name dieser Klasse CarColor lauten.

Wir könnten nun beide Klassen mit


public class Car {/*...*/}
public class CarColor {/*...*/}

festlegen, aber damit käme die eindeutige Zugehörigkeit von CarColor zu Car nicht zum Ausdruck, weil CarColor auch ohne ein zugrunde liegendes Car-Objekt instanziiert werden könnte. Wir wissen aber, dass CarColor in einer direkten Beziehung zu Car steht. Deshalb drängt sich geradezu die Idee auf, CarColor in Car zu verschachteln:


public class Car {
  public class CarColor {/*...*/}
}

Ein Objekt vom Typ CarColor soll einem Benutzer als schreibgeschützte Eigenschaft eines Car-Objekts angeboten werden. Wir ergänzen deshalb die äußere Klassendefinition Car um ein Feld, das die Referenz auf ein Car-Objekt zurückliefert:


public class Car {
  public readonly CarColor Color = new CarColor();
  public class CarColor {/*...*/}
}

Abgesehen von der internen Implementierung der Klasse CarColor können wir Car als fertig betrachten. Der gesamte weitere Programmcode beruht auf der vereinfachenden Annahme, dass sich die von jedem Hersteller angebotene Farbpalette nicht ändert. In der Praxis würde man diese im Car-Konstruktor vermutlich aus einer Datenbank beziehen. Wir legen die Farben jedoch in der Klasse CarColor fest, denn um den Einsatz der Indexer im Zusammenhang mit parameterbehafteten Eigenschaften zu verstehen, ist das völlig ausreichend.

Aus Sicht eines Benutzers sind wir nun an dieser Stelle angelangt:


Car myCar = new Car();
myCar.Color

Die zweite, noch unvollständige Anweisung liefert die Referenz auf ein CarColor-Objekt zurück. Jetzt schlägt die Stunde der Indexer! Zur Vervollständigung der Aufrufsyntax mit dem []-Operator müssen wir im nächsten Schritt in CarColor einen Indexer codieren.

Auch hier vereinfachen wir die Situation und tun so, als würde es nur zwei Autohersteller geben. Beim Aufruf der Color-Eigenschaft wird als Zeichenfolge der Hersteller übergeben. Der Rückgabewert sei ein Integer-Array, in dem jede Zahl für eine bestimmte Farbe steht.


public class CarColor {
  public int[] this[string hersteller] {
    get {
      switch(hersteller) {
        case "Rover":
          return new int[]{2, 3, 4, 5};
        case "Mazda":
          return new int[]{1, 2, 5, 6};
        default:
          return new int[]{0};
      }
    }
  }
}

Der Indexer versetzt uns jetzt in die Lage, beim Aufruf der Eigenschaft Color einen Index anzugeben, der als Argument interpretiert wird und den Eigenschaftswert maßgeblich beeinflusst.

Fassen wir den gesamten Code zusammen, und schreiben wir dazu noch zugreifenden Code, der exakt die Anweisung enthält, die Ausgangspunkt unserer Überlegungen war.


// ---------------------------------------------------------
// Beispiel: ...\Kapitel 7\Eigenschaftsparameter
// ---------------------------------------------------------
class Program {
  static string[] color = new string[]{"Fehleingabe",
                     "weiss","blau","gelb","rot","schwarz","lila"};
  static void Main(string[] args) {
    Car myCar = new Car();
    int[] arrInt = myCar.Color["Mazda"];
    for(int i = 0; i < arrInt.Length; i++)
      Console.WriteLine("Farbe {0} = {1}", i, color[arrInt[i]] );
    Console.ReadLine();
      }
   }
public class Car {
   public readonly CarColor Color = new CarColor();
   public class CarColor {
     // Indexer
     public int[] this[string hersteller] {
       get {
         switch(hersteller) {
           case "Rover":
             return new int[]{2, 3, 4, 5};
           case "Mazda":
             return new int[]{1, 2, 5, 6};
           default:
             return new int[]{0};
          }
       }
     }
   }
}

Jede der beim Aufruf der Eigenschaft Color zurückgelieferten Integer-Zahlen ist demselben numerischen Index im string-Array der Testklasse zugeordnet. Die einzige Ausnahme bildet der Index 0, der eine unzulässige Parameterübergabe an die Eigenschaft signalisiert.

Mit


int[] arrInt = myCar.Color["Mazda"];

weisen wir den Rückgabewert einem Array zu, das in der darauffolgenden for-Schleife durchlaufen wird und die herstellerspezifische Farbpalette im Befehlsfenster ausgibt.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2010

Visual C# 2010
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2010
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern