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

Inhaltsverzeichnis
Vorwort zur 6. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Das Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Strukturen und Enumerationen
7 Fehlerbehandlung und Debugging
8 Auflistungsklassen (Collections)
9 Generics – Generische Datentypen
10 Weitere C#-Sprachfeatures
11 LINQ
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 XML
15 Multithreading und die Task Parallel Library (TPL)
16 Einige wichtige .NET-Klassen
17 Projektmanagement und Visual Studio 2012
18 Einführung in die WPF und XAML
19 WPF-Layout-Container
20 Fenster in der WPF
21 WPF-Steuerelemente
22 Elementbindungen
23 Konzepte von WPF
24 Datenbindung
25 Weitere Möglichkeiten der Datenbindung
26 Dependency Properties
27 Ereignisse in der WPF
28 WPF-Commands
29 Benutzerdefinierte Controls
30 2D-Grafik
31 ADO.NET – Verbindungsorientierte Objekte
32 ADO.NET – Das Command-Objekt
33 ADO.NET – Der SqlDataAdapter
34 ADO.NET – Daten im lokalen Speicher
35 ADO.NET – Aktualisieren der Datenbank
36 Stark typisierte DataSets
37 Einführung in das ADO.NET Entity Framework
38 Datenabfragen des Entity Data Models (EDM)
39 Entitätsaktualisierung und Zustandsverwaltung
40 Konflikte behandeln
41 Plain Old CLR Objects (POCOs)
Stichwort

Download:
- Beispiele, ca. 62,4 MB

Buch bestellen
Ihre Meinung?

Spacer
Visual C# 2012 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2012

Visual C# 2012
Rheinwerk Computing
1402 S., 6., aktualisierte und erweiterte Auflage 2013, geb., mit DVD
49,90 Euro, ISBN 978-3-8362-1997-6
Pfeil 9 Generics – Generische Datentypen
Pfeil 9.1 Problembeschreibung
Pfeil 9.2 Bereitstellen einer generischen Klasse
Pfeil 9.2.1 Mehrere generische Typparameter
Pfeil 9.2.2 Vorteile der Generics
Pfeil 9.3 Bedingungen (Constraints) festlegen
Pfeil 9.3.1 Constraints mit der »where«-Klausel
Pfeil 9.3.2 Typparameter auf Klassen oder Strukturen beschränken
Pfeil 9.3.3 Mehrere Constraints definieren
Pfeil 9.3.4 Der Konstruktor-Constraint »new()«
Pfeil 9.3.5 Das Schlüsselwort »default«
Pfeil 9.4 Generische Methoden
Pfeil 9.4.1 Methoden und Constraints
Pfeil 9.5 Generics und Vererbung
Pfeil 9.5.1 Virtuelle generische Methoden
Pfeil 9.6 Konvertierung von Generics
Pfeil 9.7 Generische Delegates
Pfeil 9.7.1 Generische Delegates und Constraints
Pfeil 9.7.2 Anpassung des Beispiels »GeometricObjects«
Pfeil 9.8 Nullable-Typen
Pfeil 9.8.1 Konvertierungen mit Nullable-Typen
Pfeil 9.9 Generische Collections
Pfeil 9.9.1 Die Interfaces der generischen Auflistungsklassen
Pfeil 9.9.2 Die generische Auflistungsklasse »List<T>«
Pfeil 9.9.3 Vergleiche mit Hilfe des Delegaten »Comparison<T>«
Pfeil 9.10 Kovarianz und Kontravarianz generischer Typen
Pfeil 9.10.1 Kovarianz mit Interfaces
Pfeil 9.10.2 Kontravarianz mit Interfaces
Pfeil 9.10.3 Zusammenfassung
Pfeil 9.10.4 Generische Delegaten mit varianten Typparametern

Galileo Computing - Zum Seitenanfang

9.3 Bedingungen (Constraints) festlegenZur nächsten Überschrift


Galileo Computing - Zum Seitenanfang

9.3.1 Constraints mit der »where«-KlauselZur nächsten ÜberschriftZur vorigen Überschrift

Mit der Definition

public class Stack<T> { [...] }

teilen wir dem Compiler mit, dass der verwaltete Datentyp zur Entwicklungszeit noch unbekannt ist. Der generische Typparameter kann in diesem Fall durch jeden x-beliebigen Datentyp ersetzt werden.

Müssen Sie innerhalb des Codes der generischen Klasse jedoch ein bestimmtes Klassenmitglied des verwendeten Typs aufrufen (beispielsweise eine Methode), ist eine explizite und damit auch unsichere Konvertierung notwendig. Fehler, die eventuell auftreten, weil der verwendete Datentyp dieses Klassenmitglied nicht veröffentlicht, würden erst zur Laufzeit der Anwendung erkannt.

Um die Problematik zu verstehen, sehen Sie sich das folgende Listing 9.4 an. Die Klasse Demo<T> enthält die Methode DoSomething, die einen Parameter des Typs T definiert. Was genau die Methode leisten soll, interessiert bei dieser Betrachtung nicht. Jedoch wird innerhalb der Methode auf das an den Parameter übergebene Objekt die Methode Dispose aufgerufen, die aus der Schnittstelle IDisposable stammt.

class Demo<T> {
public void DoSomething(T param) {
[...]
param.Dispose();
[...]
}
}

Listing 9.4 Generische Klasse, die die Methode »IDisposable.Dispose« voraussetzt

Bereits das Kompilieren wird zu einem Fehler führen, da der Typparameter T die Methode Dispose nicht generell beschreibt. Wir haben hier eine Bedingung (Constraint) vorliegen, die vom Typparameter erfüllt werden muss, nämlich die Implementierung der Schnittstelle IDisposable, um die Methode Dispose zu garantieren.

Die Lösung der Problematik ist sehr einfach. Bedingungen an Typparameter werden ähnlich wie in einer SQL-Abfrage mit dem Schlüsselwort where spezifiziert. In unserem fiktiven Szenario müsste demnach die Klasse Demo folgendermaßen implementiert werden:

class Demo<T> where T : IDisposable {
public void DoSomething(T param) {
[...]
param.Dispose();
[...]
}
}

Listing 9.5 Generische Klasse mit einem Constraint

Jetzt ist eine Bedingung festgelegt, die der spätere konkrete Typ erfüllen muss: Er muss die Schnittstelle IDisposable unterstützen.

Mit einem Constraint lassen sich generische Typen einschränken, um damit vorzugeben, wie der generische Typ auszusehen hat, welche Verhaltensweisen erforderlich sind. Diese Typparameter werden auch als gebundene Typparameter bezeichnet (generische Typparameter ohne Constraints heißen entsprechend auch ungebundene Typparameter).

Dabei ist das Festlegen der Constraints äußerst flexibel und gestattet zahlreiche Möglichkeiten. So können Sie – falls erforderlich – auch mehrere Interfaces angeben, die voneinander durch ein Komma getrennt werden:

class Demo<T> where T : IDisposable, ICloneable, IComparable

Jetzt wird vorgeschrieben, dass der generische Typparameter nur durch Typen ersetzt werden kann, die gleichzeitig die drei Schnittstellen IDisposable, ICloneable und IComparable implementieren.

Eine Bedingung ist nicht nur auf Schnittstellen beschränkt. Sie können auch eine Klasse angeben und legen damit die Basisklasse des an den Typparameter T übergebenen konkreten Typs fest. Um beispielsweise vorzugeben, dass der generische Typparameter vom Typ GeometricObject (oder davon abgeleitet) sein muss, geben Sie die Klasse hinter where an:

class Demo<T> where T : GeometricObject

Sollten Sie eine Bedingung formulieren, die sowohl eine Klasse als auch eine Schnittstelle vorschreibt, muss die Angabe der Klasse vor der Schnittstelle stehen. Mehrere Klassen anzugeben ist nicht erlaubt.


Galileo Computing - Zum Seitenanfang

9.3.2 Typparameter auf Klassen oder Strukturen beschränkenZur nächsten ÜberschriftZur vorigen Überschrift

Die Angabe einer Einschränkung ist nicht nur auf konkrete Typen möglich. Sie können auch festlegen, dass der generische Typparameter entweder eine class- oder struct-Definition voraussetzt, z. B.:

class Demo<T> where T : class 
{
[...]
}

Listing 9.6 Generischer Typparameter, der auf Referenztypen beschränkt

Mit diesen Constraints lassen sich, allgemein formuliert, Werte- oder Referenztypen vorschreiben. Allerdings müssen Sie dabei berücksichtigen, dass die Bedingung struct nicht erlaubt, dass Nullable-Typen verwendet werden. Mit class ist das andererseits möglich.

Auf Nullable-Typen, die eine weitere Spielart der Generics sind, werden wir in Abschnitt 9.8 noch eingehen.


Galileo Computing - Zum Seitenanfang

9.3.3 Mehrere Constraints definierenZur nächsten ÜberschriftZur vorigen Überschrift

Beschreibt eine Klasse mehrere generische Typparameter, lassen sich Bedingungen für jeden einzelnen generischen Typparameter festlegen. Dazu müssen Sie den Constraint für jeden einzelnen Platzhalter mit where einleiten:

public class Demo<T, A> where T : IComparable, ICloneable 
where A : IDisposable
{
[...]
}

Listing 9.7 Mehrere generische Typparameter einschränken


Galileo Computing - Zum Seitenanfang

9.3.4 Der Konstruktor-Constraint »new()«Zur nächsten ÜberschriftZur vorigen Überschrift

Nehmen wir an, Sie möchten in einer generischen Klasse ein Objekt vom Typ des generischen Typparameters erzeugen. Das Problem dabei ist, dass der C#-Compiler nicht weiß, ob die den Typparameter ersetzende Klasse einen passenden Konstruktor hat. Die Folge wäre ein Kompilierfehler. Um in dieser Situation eine Lösung zu bieten, können Sie an die Liste der Constraints new() anhängen, wie im folgenden Codefragment gezeigt wird:

public class Demo<T> where T : new() 
{
public T DoSomething() {
return new T();
}
}

Listing 9.8 Generischer Typparameter, der den parameterlosen Konstruktor vorschreibt

Der generische Typparameter kann nunmehr nur durch Objekte konkretisiert werden, die einen öffentlichen, parameterlosen Konstruktor unterstützen. Einen parametrisierten Konstruktor vorzuschreiben ist nicht möglich. Werden mehrere Bedingungen definiert, steht new() grundsätzlich immer am Ende der Aufzählung.


Galileo Computing - Zum Seitenanfang

9.3.5 Das Schlüsselwort »default«Zur vorigen Überschrift

Im Beispiel GenerischerStack wird eine Exception ausgelöst, wenn die Methode Pop aufgerufen wird und der Stack leer ist. Eine andere Lösung hätte vermutlich auch zum Ziel geführt: die Rückgabe mit return.

public T Pop() {
pointer--;
if (pointer >= 0)
return elements[pointer];
else {
pointer = 0;

// Problemfall: der Rückgabewert

return null;
}
}

Listing 9.9 Rückgabewert der Methode »Pop« der »Stack<T>«-Klasse

Dieser Ansatz ist richtig, solange der Typparameter durch einen Referenztyp beschrieben wird. Handelt es sich jedoch um einen Wertetyp, wird die Laufzeit in einem Desaster enden, da einem Wertetyp null nicht zugewiesen werden kann; die Rückgabe muss dann 0 sein. Andererseits kann bei Referenztypen nicht einfach der Wert 0 zurückgeliefert werden, denn hier muss es null sein.

Die Lösung des Problems führt über das C#-Schlüsselwort default. Dieses kann zwischen Referenz- und Wertetypen unterscheiden und liefert null, wenn es sich bei dem konkreten Typ um einen Referenztyp handelt, bzw. 0, wenn es ein den Wertetypen zugerechneter Typ ist.

public T Pop() {
pointer--;
if (pointer >= 0)
return elements[pointer];
else {
pointer = 0;
return default(T);
}
}

Listing 9.10 Rückgabewert »default(T)«



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# 2012

Visual C# 2012
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 2013
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.


[Rheinwerk Computing]

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