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 5 Delegates und Ereignisse
Pfeil 5.1 Delegates
Pfeil 5.1.1 Einführung in das Prinzip der Delegates
Pfeil 5.1.2 Verwendung von Delegates
Pfeil 5.1.3 Vereinfachter Delegatenaufruf
Pfeil 5.1.4 Multicast-Delegates
Pfeil 5.1.5 Anonyme Methoden
Pfeil 5.1.6 Kovarianz und Kontravarianz mit Delegaten
Pfeil 5.2 Ereignisse eines Objekts
Pfeil 5.2.1 Ereignisse bereitstellen
Pfeil 5.2.2 Die Reaktion auf ein ausgelöstes Ereignis
Pfeil 5.2.3 Allgemeine Betrachtungen der Ereignishandler-Registrierung
Pfeil 5.2.4 Wenn der Ereignisempfänger ein Ereignis nicht behandelt
Pfeil 5.2.5 Ereignisse mit Übergabeparameter
Pfeil 5.2.6 Ereignisse in der Vererbung
Pfeil 5.2.7 Hinter die Kulissen des Schlüsselworts »event« geblickt
Pfeil 5.2.8 Die Schnittstelle »INotifyPropertyChanged«
Pfeil 5.3 Änderungen im Projekt »GeometricObjects«
Pfeil 5.3.1 Überarbeitung des Events »InvalidMeasure«
Pfeil 5.3.2 Weitere Ereignisse

5 Delegates und EreignisseZur nächsten Überschrift


Galileo Computing - Zum Seitenanfang

5.1 DelegatesZur nächsten ÜberschriftZur vorigen Überschrift


Galileo Computing - Zum Seitenanfang

5.1.1 Einführung in das Prinzip der DelegatesZur nächsten ÜberschriftZur vorigen Überschrift

»Delegate« ist das englische Wort für »Delegierter«, also für jemanden, der einen Auftrag weiterleiten soll. Tatsächlich leitet ein Delegat weiter, er leitet nämlich einen Methodenaufruf an eine bestimmte Methode weiter. Die Technik, die sich dahinter verbirgt, wird in der Sprache C auch als Funktionszeiger bezeichnet. Wie Sie wissen, basiert ausnahmslos alles in .NET auf Objekten. Da verwundert es nicht, dass auch die Funktionszeiger in ein Objekt verpackt und als Delegate bezeichnet ihren Weg in die Laufzeitumgebung finden.

Ein Delegate ist ein Typ, der den Zeiger auf eine Methode beschreibt.

Bevor wir uns mit den Details von Delegaten beschäftigen, wollen wir uns zunächst an einem einfachen Beispiel die grundsätzliche Arbeitsweise verdeutlichen.

Die Operation, die von diesem Code ausgeführt wird, ist recht einfach: Der Anwender gibt zwei Zahlen an der Konsole ein und hat anschließend die Wahl, ob beide Zahlen addiert oder subtrahiert werden sollen. Das Resultat der Operation wird abhängig von der Wahl des Anwenders an der Konsole ausgegeben.

// Beispiel: ..\Kapitel 5\SimpleDelegate

public delegate double CalculateHandler(double value1, double value2);

class Program {
static void Main(string[] args) {

// Variable vom Typ des Delegaten

CalculateHandler calculate;

do {

// Eingabe der Operation

Console.Clear();
Console.Write("Geben Sie den ersten Operanden ein: ");
double input1 = Convert.ToDouble(Console.ReadLine());
Console.Write("Geben Sie den zweiten Operanden ein: ");
double input2 = Convert.ToDouble(Console.ReadLine());

// Wahl der Operation

Console.Write("Operation: Addition - (A) oeder Subtraktion - (S)? ");
string wahl = Console.ReadLine().ToUpper();

// In Abhängigkeit von der Wahl des Anwenders wird die Variable 'calculate'

// mit einem Zeiger auf die auszuführende Methode initialisiert

if (wahl == "A")
calculate = new CalculateHandler(Demo.Add);
else if (wahl == "S")
calculate = new CalculateHandler(Demo.Subtract);
else {
Console.Write("Ungültige Eingabe");
Console.ReadLine();
return;
}

// Aufruf der Operation 'Add' oder 'Subtract' über den Delegaten

double result = calculate(input1, input2);
Console.WriteLine("----------------------------------");
Console.WriteLine("Ergebnis = {0}\n\n", result);
Console.WriteLine("Zum Beenden F12 drücken.");
} while (Console.ReadKey(true).Key != ConsoleKey.F12);
}

}

class Demo {
public static double Add(double x, double y)
{
return x + y;
}

public static double Subtract(double x, double y) {
return x - y;
}
}

Listing 5.1 Das Beispielprogramm »SimpleDelegate«

In der Klasse Demo sind zwei statische Methoden definiert, die aus Main heraus aufgerufen werden und die beiden Operationen Add und Subtract beschreiben. Die Wahl, ob die beiden Zahlen addiert oder subtrahiert werden sollen, trifft der Anwender durch die Eingabe von »A« oder »S« an der Konsole. Um die Eingabe in Kleinschreibweise ebenfalls zu berücksichtigen, wird die Eingabe mit der Methode ToUpper der Klasse String in Großschreibweise umgewandelt.

string wahl = Console.ReadLine().ToUpper();

Nachdem der Anwender seine Wahl getroffen hat, wird geprüft, wie diese ausgefallen ist, um entsprechend im Programmcode zu reagieren. Vermutlich hätten Sie eine solche Aufgabenstellung bisher wie folgt gelöst:

double result;
if(wahl == "A")
result = Demo.Add(input1, input2);
else if(wahl == "S")
result = Demo.Subtract(input1, input2);
[...]

Es gibt keinen Zweifel daran, dass diese Implementierung natürlich auch zum richtigen Ergebnis führt. Das Resultat der Operation wird in den Anweisungsblöcken hinter if bzw. else if abgerufen.

Nun betrachten wir die entscheidenden Anweisungen der Lösung im Beispiel SimpleDelegate:

if(wahl == "A")
calculate = new CalculateHandler(Demo.Add);
else if(wahl == "S")
calculate = new CalculateHandler(Demo.Subtract);
[...]

Das Ergebnis der Addition bzw. Subtraktion wird nun nicht mehr in den beiden Anweisungsblöcken der if-Struktur abgerufen, sondern außerhalb derselben mit der Anweisung

double result = calculate(input1, input2);

Da außerhalb der if-Struktur die Wahl des Anwenders nicht bekannt sein kann, stehen wir vor der Frage, wie es möglich ist, die gewünschte Methode aufzurufen. Die Antwort darauf ist prinzipiell nicht schwierig: Wir müssen in einer Variablen die Laufzeitadresse auf die entsprechende Methode vorhalten, also einen Verweis konstruieren. Dieser kann später an einer beliebigen Stelle im Code ausgewertet werden.

Bisher kennen wir Verweise nur im Zusammenhang mit Objekten. Mit Objektverweisen werden zusammenhängende Datenblöcke im Hauptspeicher adressiert, in denen die Zustandsdaten eines ganz bestimmten Objekts beschrieben werden. Ein Verweis auf Programmcode ist im Grunde genommen nicht anders, zeigt aber auf Bytesequenzen, die anders interpretiert werden müssen – nämlich als ausführbarer Programmcode. Damit ist auch klar, dass ein Verweis auf Code anders definiert werden muss als ein Verweis auf Datenblöcke, den wir bisher immer verwendet haben. Aus diesem Grund wurden in .NET die Delegates eingeführt.

Wie oben erwähnt, kapselt ein Delegat den Zeiger auf eine Methode, beschreibt also eine Speicheradresse. Sehen wir uns jetzt an, wie diese Anforderung gelöst wird. Im Code des Beispiels SimpleDelegate wird mit

public delegate double CalculateHandler(double value1, double value2);

ein Delegat definiert. Diese Definition erinnert ein wenig an die Signatur einer Methode namens CalculateHandler, die zwei Parameter vom Typ double empfängt und als Rückgabewert einen double liefert – nur ergänzt um das Schlüsselwort delegate.

Ein Delegat-Objekt kapselt den Zeiger auf eine Methode – oder mit anderen Worten: Er steht für einen beliebigen Methodenaufruf. Ganz beliebig ist der Methodenaufruf allerdings nicht, denn jede Methode hat eine exakt definierte Parameterliste mit Parametern eines bestimmten Typs. Ein Delegat beschreibt einen Zeiger auf eine Methode, wobei die Typen der Parameterliste der Methode, auf die der Delegat zeigt, mit der Parameterliste der delegate-Definition übereinstimmen müssen.

In unserem Beispiel werden in der Parameterliste des Delegates CalculateHandler zwei Parameter vom Typ double aufgeführt. Damit ist ein Delegat-Objekt in der Lage, jede x-beliebige Methode eines x-beliebigen Objekts aufzurufen – vorausgesetzt, die Methode definiert eine Parameterliste, die genau zwei double-Argumente erwartet. Die adressierte Methode darf natürlich auch statisch sein oder sich in einer anderen Klasse befinden – das alles spielt keine Rolle. Die einzige Bedingung ist, dass die durch den Delegaten beschriebene Methode mit Programmcode angesprochen werden kann, also erreichbar ist.

Neben der Parameterliste spielt auch der Rückgabewert eine entscheidende Rolle. Im Beispiel des Delegates CalculateHandler muss die Methode in jedem Fall einen Rückgabewert vom Typ double haben.

Nicht jede Methode hat einen Rückgabewert. Beabsichtigen Sie beispielsweise, einen Delegaten zu definieren, der in der Lage ist, einen Zeiger auf sämtliche Methoden zu beschreiben, die parameterlos sind und keinen Rückgabewert haben, sähe die Definition folgendermaßen aus:

public delegate void MyDelegate();

Sie können die Definition eines Delegates mit der Definition einer Klasse vergleichen, denn beide beschreiben einen Typ. Um ein konkretes Objekt zu erhalten, muss zuerst eine Variable vom Typ der Klasse deklariert werden – das ist bei einem Delegaten nicht anders. Im Beispiel oben dient dazu folgende Anweisung:

CalculateHandler calculate;

Damit ist die Variable calculate vom Typ CalculateHandler deklariert, aber noch nicht initialisiert. Mit anderen Worten: calculate ist ein Delegat und kann auf eine Methode zeigen, die zwei double-Argumente erwartet und einen double als Resultat des Aufrufs zurückliefert. In diesem Moment weiß der Delegate allerdings noch nicht, um welche Methode es sich dabei genau handelt.

Die Initialisierung erfolgt – analog zur Instanziierung einer Klasse – mit dem Operator new unter Angabe des Delegattyps. Delegates haben nur einen einfach parametrisierten Konstruktor, der den Bezeichner der Methode erwartet, die später aufgerufen werden soll. In unserem Beispiel handelt es sich um

calculate = new CalculateHandler(Demo.Add);

bzw. um

calculate = new CalculateHandler(Demo.Subtract);

Jetzt ist dem Delegate bekannt, welche Methode ausgeführt werden soll: entweder Add oder Subtract. Allerdings wird die Methode, auf die der Delegat in Form eines Zeigers verweist, noch nicht sofort ausgeführt, denn dazu bedarf es eines Anstoßes durch den Aufruf des Delegates:

double result = calculate(input1, input2);

Der Aufruf erinnert an den Aufruf einer Methode, dabei wird allerdings der Methodenname durch die Variable vom Typ des Delegates ersetzt. In den Klammern werden die erforderlichen Argumente an die Methode übergeben.

Damit die Anwendung nicht schon nach der ersten Berechnung beendet wird, ist die gesamte Programmlogik der Methode Main in einer do/while-Schleife codiert. Im Schleifenfuß erfolgt eine Überprüfung, ob der Anwender die Anwendung beenden möchte. Dazu muss er die Taste F12 drücken.

do {

// Anweisungen

} while (Console.ReadKey(true).Key != ConsoleKey.F12);

Die statische Methode ReadKey ruft dabei die gedrückte Zeichen- oder Funktionstaste ab. Das Übergabeargument true sagt aus, dass das entsprechende Zeichen nicht in die Konsole geschrieben werden soll. Der Rückgabewert der Methode ReadKey ist vom Typ ConsoleKeyInfo. Darauf rufen wir mit der Eigenschaft Key die gedrückte Taste ab, die über die Konstantenauflistung ConsoleKey beschrieben wird.


Galileo Computing - Zum Seitenanfang

5.1.2 Verwendung von DelegatesZur nächsten ÜberschriftZur vorigen Überschrift

Nachdem wir uns im letzten Abschnitt mit dem Einsatz der Delegates beschäftigt haben, steht nunmehr die Frage im Raum, wann Delegates verwendet werden. Grundsätzlich kann man sagen, dass Delegates genau dann verwendet werden, wenn zur Entwicklungszeit noch nicht bekannt ist, wie die Methode heißt, die ausgeführt werden soll. Um es noch einmal zu erwähnen: Von der aufzurufenden Methode ist nur die Parameterliste und der Typ der Rückgabe vorgeschrieben.

Ein typisches Verwendungsgebiet werden Sie in Kapitel 15 noch kennenlernen: Es sind Threads und die zu diesem Themenkomplex gehörenden asynchronen Methodenaufrufe. Aber insbesondere wenn Sie sich mit der Entwicklung grafischer Benutzerschnittstellen beschäftigen sollten (WinForm-Anwendungen, WPF-Anwendungen, ASP.NET usw.), kommen Sie an Delegaten nicht vorbei – obwohl Delegates hier unter dem Begriff »Ereignis« verwendet werden. Darüber erfahren Sie in Abschnitt 5.2 alles Wesentliche.


Galileo Computing - Zum Seitenanfang

5.1.3 Vereinfachter DelegatenaufrufZur nächsten ÜberschriftZur vorigen Überschrift

Es gibt noch eine weitere Notation, um einen Delegaten zu instanziieren und ihm gleichzeitig die auszuführende Methode anzugeben. Diese ist etwas einfacher in der Handhabung und erspart uns etwas Tipparbeit. Sie können nämlich anstelle der Anweisung

CalculateHandler calculate = new CalculateHandler(Demo.Addition);

auch wie folgt codieren:

CalculateHandler calculate = Demo.Add;

Galileo Computing - Zum Seitenanfang

5.1.4 Multicast-DelegatesZur nächsten ÜberschriftZur vorigen Überschrift

.NET bietet die Möglichkeit, mehrere Delegates zu einem einzigen zusammenzufassen. Dadurch entsteht ein Delegaten-Verbund, der auch als Multicast-Delegates bezeichnet wird. Der Vorteil ist, dass durch den Aufruf eines Delegates mehrere Delegates der Reihe ausgeführt werden können.

Es gibt zwei Möglichkeiten, um aus einem Delegaten einen Multicast-Delegaten zu machen:

  • die Methode Combine der Klasse Delegate
  • der +=-Operator

Die Methode Combine wird selten verwendet. Vielmehr treffen Sie in der Regel auf den genannten Operator. Dennoch werden wir uns auch kurz mit der Methode auseinandersetzen.

Die Methode »Combine()«

Mit der statischen Methode Combine der Klasse Delegate lassen sich mehrere Delegates miteinander verknüpfen. Die Methode ist wie folgt überladen:

public static Delegate Combine(Delegate[]);

public static Delegate Combine(Delegate del1, Delegate del2);

Sie können als Argument entweder ein Array vom Typ Delegate übergeben oder haben die Alternative, zwei Delegates anzugeben. Der Rückgabewert ist in beiden Fällen vom Typ Delegate. An einem Beispiel sehen wir uns das nun auch an.

// Beispiel: ..\Kapitel 5\MulticastDelegateSample1

public delegate void MyDelegate();

class Program {
static void Main(string[] args) {
MyDelegate del = (MyDelegate)Delegate.Combine(new MyDelegate(DoSomething),
new MyDelegate(DoSomethingMore));

// Multicast-Delegaten ausführen

del();
Console.ReadLine();
}

public static void DoSomething() {
Console.WriteLine("In der Methode 'DoSomething'");
}

public static void DoSomethingMore() {
Console.WriteLine("In der Methode 'DoSomethingMore'");
}
}

Listing 5.2 Das Beispielprogramm »MulticastDelegateSample1«

In der Klasse Program sind mit DoSomething und DoSomethingMore zwei statische, parameterlose Methoden definiert. In Main wird mit del ein MyDelegate-Objekt erzeugt. Dazu wird die Methode Combine aufgerufen, und dabei werden zwei konkrete Delegates übergeben, die auf die beiden Methoden DoSomething und DoSomethingMore verweisen. Da Combine eine Delegate-Referenz zurückliefert, muss diese noch in den tatsächlichen Typ umgewandelt werden. Anschließend wird del ausgeführt. In der Konsolenausgabe ist zu erkennen, dass beide Methoden ausgeführt werden. Beachten Sie dabei auch, dass dem resultierenden MyDelegate-Objekt del nicht anzusehen ist, dass es intern zwei – oder auch noch mehr – andere Delegates beschreibt.

Angemerkt sei hier noch abschließend, dass sich mit Remove ein Delegate aus der Liste auch wieder entfernen lässt.

Der »+=«-Operator

Die Combine-Methode einzusetzen, ist nicht besonders schwierig. Aber uns steht noch eine andere Variante zur Verfügung, die kürzer und damit wohl auch besser lesbar ist. Es handelt sich um den Operator »+=«. Um das zu demonstrieren, soll die Main-Methode des Beispielprogramms MulticastDelegateSample1 entsprechend umgeschrieben werden.

// Beispiel: ..\Kapitel 5\MulticastDelegateSample2

static void Main(string[] args) {
MyDelegate del = new MyDelegate(DoSomething);
del += new MyDelegate(DoSomethingMore);

// Multicast-Delegaten ausführen

del();
Console.ReadLine();
}

Listing 5.3 Das Beispielprogramm »MulticastDelegateSample2«

Analog zur Methode Remove lassen sich Delegaten mit dem -=-Operator auch aus der Aufrufliste der Delegaten entfernen. Beachten Sie bitte unbedingt, dass Sie beim Hinzufügen eines weiteren Delegaten zu einem bestehenden auch wirklich den +=-Operator verwenden. Mit dem einfachen Zuweisungsoperator würden Sie nämlich nur die bestehende Aufrufliste durch einen anderen Delegaten komplett ersetzen.

Wenn Sie möchten, können Sie die Zuweisungen im Beispielprogramm MulticastDelegateSample2 auch noch kürzer schreiben:

MyDelegate del = DoSomething;
del += DoSomethingMore;

Ich selber bin nicht unbedingt ein Fan dieser Schreibweise, die meiner Meinung nach einen nicht unbedingt besser lesbaren Code produziert.


Galileo Computing - Zum Seitenanfang

5.1.5 Anonyme MethodenZur nächsten ÜberschriftZur vorigen Überschrift

Delegates haben wir bisher konstruiert, indem wir den Delegate instanziiert und dem Konstruktor einen Methodenbezeichner übergeben haben. Das setzt voraus, dass die Methode im Programmcode namentlich bekannt ist.

Es geht aber auch noch anders, wenn wir mit einer anonymen Methode arbeiten. Unter einer anonymen Methode wird ein Anweisungsblock verstanden, der nicht über einen Methodenbezeichner namentlich aufrufbar ist. Wir wollen uns das an einem Beispiel ansehen und ändern dazu das Beispielprogramm SimpleDelegate so ab, dass anstelle der Methoden Add und Subtract nun anonyme Methoden verwendet werden.

// Beispiel: ..\Kapitel 5\AnonymeMethoden

public delegate double CalculateHandler(double value1, double value2);

class Program {
static void Main(string[] args)
{

// Variable vom Typ des Delegaten

CalculateHandler calculate;
do {

// Eingabe der Operanden

Console.Clear();
Console.Write("Geben Sie den ersten Operanden ein: ");
double input1 = Convert.ToDouble(Console.ReadLine());
Console.Write("Geben Sie den zweiten Operanden ein: ");
double input2 = Convert.ToDouble(Console.ReadLine());

// Wahl der Operation

Console.Write("Operation: Addition - (A) oder Subtraktion - (S)? ");
string wahl = Console.ReadLine().ToUpper();
if (wahl == "A")
calculate = delegate(double x, double y)
{
return x + y;
};

else if (wahl == "S")
calculate = delegate(double x, double y)
{
return x - y;
};

else
{
Console.Write("Ungültige Eingabe");
Console.ReadLine();
return;
}
double result = calculate(input1, input2);
Console.WriteLine("----------------------------------");
Console.WriteLine("Ergebnis = {0}\n\n", result);
Console.WriteLine("Zum Beenden F12 drücken.");
} while (Console.ReadKey(true).Key != ConsoleKey.F12);
}
}

Listing 5.4 Das Beispielprogramm »AnonymeMethoden«

Der Code, der vorher noch in den Methoden Add und Subtract implementiert war, wird nun direkt nach der Deklaration der Variablen vom Typ des Delegates angegeben.

if (wahl == "A")
calculate = delegate(double x, double y)
{
return x + y;
};

Das Schlüsselwort delegate dient dazu, einen Delegate zu instanziieren und das Objekt direkt mit einer anonymen Methode zu verbinden. Hinter delegate ist die Parameterliste entsprechend der Delegate-Definition angegeben. Handelt es sich um eine parameterlose, anonyme Methode, bleibt die Liste leer, und man kann auf die Angabe der runden Klammern verzichten.

Da sich der Anweisungsblock einer anonymen Methode immer innerhalb einer anderen Methode befindet, kann aus der anonymen Methode heraus auf jede andere Variable der umgebenden Methode zugegriffen werden.

Anonyme Methoden unterliegen im Vergleich zu anderen Anweisungsblöcken nur einer Einschränkung: Mit den Sprunganweisungen continue, break und goto darf innerhalb einer anonymen Methode nicht zu einer Anweisung verzweigt werden, die außerhalb der anonymen Methode codiert ist. Ebenfalls unzulässig ist eine Sprunganweisung außerhalb einer anonymen Methode, deren Ziel innerhalb einer anonymen Methode zu finden ist.

Vielleicht werden Sie sich an dieser Stelle sagen: »Brauche ich nicht. Der Code ist dadurch viel schlechter zu lesen und zu interpretieren«. Mit Ihrem bisherigen Kenntnisstand ist das nachvollziehbar. Aber den .NET-Architekten fällt immer wieder etwas Neues ein – auch hinsichtlich der anonymen Methoden, die mit .NET 2.0 eingeführt worden sind. Bei den später eingeführten Neuerungen handelt es sich um die sogenannten Lambda-Ausdrücke, die ich Ihnen in Kapitel 10 noch erklären werde. In diesem Zusammenhang werden wir uns noch einmal mit den anonymen Methoden beschäftigen.


Galileo Computing - Zum Seitenanfang

5.1.6 Kovarianz und Kontravarianz mit DelegatenZur nächsten ÜberschriftZur vorigen Überschrift

Delegaten sind typsichere Methodenzeiger. Jede Methode, die der Signatur des Delegaten entspricht, kann dem Delegaten zugewiesen werden. Sobald der Delegate auf eine Methode zeigt, verhält er sich so wie die Methode.

Bereits mit .NET 3.5 wurde die sogenannte Varianzunterstützung in allen Delegaten eingeführt. Das bedeutet, dass nicht nur Methoden, die über eine exakt übereinstimmende Signatur hinsichtlich der Parametertypen und des Rückgabedatentyps verfügen, einem Delegaten übergeben werden können, sondern auch davon abgeleitete Typen. Dabei wird zwischen der Kovarianz (Flexibilität hinsichtlich des Rückgabedatentyps) und der Kontravarianz (Flexibilität hinsichtlich der Parameterdatentypen) unterschieden. Das klingt kompliziert, daher wollen wir uns auch sofort entsprechende Beispiele ansehen.

Delegates und Kovarianz

Von Kovarianz wird gesprochen, wenn der Datentyp der Rückgabe einer Methode einen höheren Ableitungsgrad hat als der, den der Delegate definiert. Beispielsweise hat in unserem Beispielprogramm GeometricObjects die Klasse Circle einen höheren Ableitungsgrad als die Klasse GeometricObject.

Das folgende Beispiel demonstriert die Kovarianz. Auch hier spielen die beiden Klassen GeometricObject und Circle eine Rolle. Dabei kommt es aber nicht auf die Implementierung selbst an, so dass die Klassen keinen weiteren Programmcode enthalten.

// Beispiel: ..\Kapitel 5\KovarianzSample

class GeometricObject { }
class Circle : GeometricObject { }
delegate GeometricObject CovarianceHandler();

class Program {

static void Main(string[] args)
{
CovarianceHandler handler = DoSomething;
GeometricObject geo = handler();
Console.WriteLine(geo.GetType());
Console.ReadLine();
}

public static Circle DoSomething(){
return new Circle();
}
}

Listing 5.5 Beispiel zur Kovarianz von Delegates

Richten Sie Ihr Augenmerk in dem Beispiel zuerst auf die Definition des Delegates, der eine parameterlose Methode beschreibt, der Rückgabewert vom Typ GeometricObject ist:

delegate GeometricObject CovarianceHandler();

In der Klasse Program ist neben Main mit DoSomething noch eine weitere Methode definiert. Interessant ist, dass die Methode einen Rückgabewert vom Typ Circle beschreibt.

public static Circle DoSomething() {
return new Circle();
}

Nun analysieren wir noch die Anweisung

CovarianceHandler handler = DoSomething;

Der Handler beschreibt ein GeometricObject, die Methode DoSomething liefert ein Circle-Objekt, das selber ein GeometricObject ist. Da die Zuweisung der Referenz eines abgeleiteten Typs an die Referenz eines Basistyps mit der impliziten Konvertierung möglich ist, kann DoSomething dank der Kovarianz durch den beschriebenen Delegaten referenziert werden.

Kontravarianz mit Delegates

Kontravarianz ist der Kovarianz sehr ähnlich, bezieht sich aber nicht auf die Rückgabedatentypen, sondern auf die Datentypen der Parameter. Kontravarianz ermöglicht uns, Delegates zu benutzen, deren vorgeschriebener Parametertyp abgeleitet ist von dem Parametertyp der Methode, die der Delegate referenziert. Verständlich wird der Sachverhalt, wenn wir uns auch hierzu ein Beispiel ansehen, das mit den Klassen GeometricObject und Circle sehr ähnlich dem vorhergehenden Beispiel aufgebaut ist.

// Beispiel: ..\Kapitel 5\KontravarianzSample

class GeometricObject { }
class Circle : GeometricObject { }
delegate void ContravarianceHandler(Circle circle);

class Program
{
static void Main(string[] args)
{
ContravarianceHandler handler = DoSomething;
handler(new Circle());
Console.ReadLine();
}

public static void DoSomething(GeometricObject geoObject) { }

}

Listing 5.6 Beispiel zur Kontravarianz von Delegates

Der Delegate definiert in diesem Beispiel einen Parameter vom Typ Circle. Die Methode, die das Delegate-Objekt in Main referenziert, beschreibt in ihrem Parameter mit GeometricObject einen Basistyp von Circle. Hier spiegelt sich genau das wider, was auch für alle Methoden gilt: Man kann an den Parameter einer Methode ein Objekt übergeben, das vom gleichen Typ ist wie der Parameter oder davon abgeleitet.



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