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.3 Operatorüberladung Zur nächsten ÜberschriftZur vorigen Überschrift

C# verfügt über eine Reihe von Operatoren, die Sie für allgemeine Operationen einsetzen können. Werden zwei Zahlen dividiert, müssen Sie sich keine Gedanken darüber machen, welcher Code im Hintergrund vom Compiler erzeugt wird:


double ergebnis = value1 / value2;

Die Frage nach dem Typ der Operanden ist nicht bedeutungslos. Handelt es sich um ganzzahlige Typen, wird ein anderes Kompilat erzeugt, als würde es sich um zwei Dezimalzahlen handeln. Abhängig vom Typ der Operanden werden intern zwei unterschiedliche Operationen ausgeführt. Der Compiler entscheidet, um welche Operation es sich dabei handelt, denn der /-Operator ist überladen. Insbesondere für die elementaren Datentypen wie int, long, double usw. sind die meisten Operatoren überladen.

Eine der großen Stärken von C# ist, dem Entwickler das Instrumentarium an die Hand zu geben, um im Bedarfsfall Operatoren benutzerdefiniert zu überladen.


Galileo Computing - Zum Seitenanfang

7.3.1 Syntax der Operatorüberladung Zur nächsten ÜberschriftZur vorigen Überschrift

Um Operatoren in einer Klasse oder einer Struktur zu überladen, stellt C# das Schlüsselwort operator zur Verfügung, das nur in Verbindung mit public static verwendet werden darf. Hinter dem operator-Schlüsselwort wird der Operator angegeben, der überladen werden soll. Die folgende Syntax gilt für binäre Operatoren, die zwei Operanden für ihre Operation benötigen:


public static <Ergebnistyp> operator <Operator> (<Operand1>, <Operand2>)

Neben den binären gibt es auch unäre Operatoren, die nur einen Operanden verlangen. Stellvertretend seien hier die Operatoren ++ und -- genannt. Für diese Operatorengruppe ändert sich die Syntax wie folgt:


public static <Ergebnistyp> operator <Operator> (<Operand>)

Wenn Sie eine Klasse um Methoden zur Operatorüberladung erweitern, sollten Sie folgende Punkte berücksichtigen:

  • Es können nur vordefinierte Operatoren überladen werden. Neue Operatoren zu »erfinden« ist nicht möglich.
  • Die Operationen von Operatoren auf den systemeigenen Typen können nicht umdefiniert werden.
  • Die Grundfunktionalität eines Operators bleibt immer erhalten: Ein binärer Operator benötigt immer zwei Operanden, ein unärer immer einen. Die Vorrangregeln können nicht beeinflusst werden.

In Tabelle 7.3 sind alle Operatoren aufgeführt, die in einer Klasse oder Struktur überladen werden dürfen.


Tabelle 7.3 Überladbare Operatoren

C#-Operator Bedeutung

+, -, !, ~, ++, --, true, false

unäre Operatoren

+, -, *, /, %, &, |, ^, <<, >>

binäre Operatoren

==, !=, <, >, <=, >=

relationale Operatoren

[]

Dieser Operator kann eigentlich nicht überladen werden. Es gibt jedoch ein Ersatzkonstrukt (Indexer), das die gleiche Funktionalität bietet (siehe Abschnitt 7.4).


Einige Operatoren können nur paarweise überladen werden. Wollen Sie zum Beispiel den Vergleichsoperator == überladen, müssen Sie auch den Operator != überladen. Damit erzwingt C# eine konsistente Prüfung auf Übereinstimmung und Nichtübereinstimmung.

Einschränkungen der Operatorüberladung

Nicht alle Operatoren sind überladungsfähig. Ausgeschlossen ist unter anderem auch der Zuweisungsoperator =. Überladen Sie einen binären Operator, z. B. +, wird der Additionszuweisungsoperator += automatisch implizit überladen.

Zu den anderen nichtüberladbaren Operatoren gehören der Punktoperator, der bedingte Operator ?: sowie die Operatoren new, is, typeof und sizeof. Ebenso wenig überladbar sind die runden Klammern, mit denen eine Typkonvertierung durchgeführt wird. Stattdessen sollten benutzerdefinierte Konvertierungen codiert werden. Dieses Thema beschäftigt uns weiter unten.


Galileo Computing - Zum Seitenanfang

7.3.2 Operatorüberladungen in »GeometricObjectsSolution« Zur nächsten ÜberschriftZur vorigen Überschrift

Wir wollen uns die Operatorüberladung jetzt an einem Beispiel ansehen. Dazu rufen wir uns die Methode Bigger der Klasse GeometricObject in Erinnerung:


public static int Bigger(GeometricObject geoObj1, GeometricObject geoObj2)
{
  if (geoObj1.GetArea() < geoObj2.GetArea())
    return -1;
  else if (geoObj1.GetArea() == geoObj2.GetArea())
    return 0;
  return 1;
}

Wenn wir zwei Circle-Objekte übergeben, können wir zweifelsfrei feststellen, welches der beiden größer ist als das andere. Selbstkritisch müssen wir aber auch feststellen, dass der Ausdruck


if(kreis1 >= kreis2)

eher einer üblichen Vergleichsoperation entspricht. Bisher ist diese Vergleichsoperation jedoch nicht möglich, weil sie für Objekte vom Typ der Basisklasse GeometricObject oder in einer der abgeleiteten Klassen nicht definiert ist. Um dieses Defizit auszugleichen, wollen wir jetzt den >=-Operator so überladen, dass er zur Laufzeit auf zwei Objekte vom Typ GeometricObject angewendet werden kann:


// Operatorüberladung
public static bool operator >=(GeometricObject geoObj1,
                               GeometricObject geoObj2) {
  if(geoObj1.GetArea() >= geoObj2.GetArea())
    return true;
  return false;
}

Kompilieren wir die so ergänzte Klassendefinition, erhalten wir einen Compilerfehler, weil sich ein GeometricObject-Objekt jetzt nicht mehr eindeutig verhält. Wir werden gezwungen, einen weiteren Vergleichsoperator zu überladen, nämlich den, der die Umkehrung der überladenen Vergleichsfunktion beschreibt.


public static bool operator <=(GeometricObject geoObj1,
                               GeometricObject geoObj2) {
  if(geoObj1.GetFlaeche() <= geoObj2.GetFlaeche())
    return true;
  return false;
}

Nach dem anschließenden erfolgreichen Kompilieren können wir mit


Circle kreis1 = new Circle(6);
Circle kreis2 = new Circle(3);
if(kreis1 >= kreis2) {
  // Anweisungen
}

alternativ zu der von uns implementierten Methode Bigger Vergleichsoperationen mit Objekten unserer Klasse ausführen. Da die Überladung für alle Objekte vom Typ GeometricObject gilt, lässt sich auch ein Circle mit einem Rectangle vergleichen.

Überladen von Gleichheitsoperatoren

Im Abschnitt zuvor haben wir die Operatoren <= und >= überladen, um die Größe zweier geometrischer Objekte miteinander zu vergleichen. Vielleicht ist Ihnen aufgefallen, dass bisher der Vergleich mit dem ==-Operator nicht bereitgestellt worden ist. Das wollen wir nun tun, aber dazu muss ich ein wenig ausholen.

Jede Klasse beerbt die Klasse Object. Damit hat jedes Objekt die Methoden, die in Objekt definiert sind. Zu diesen Methoden gehört auch die Methode Equals, mit der von Haus aus zwei Referenzen auf Gleichheit untersucht werden können. Das wollen wir uns an einem kleinen Beispiel zuerst ansehen:


Circle kreis1 = new Circle(12);
Circle kreis2 = kreis1;
if(kreis1.Equals(kreis2))
  Console.WriteLine("Referenzielle Gleichheit");
else
  Console.WriteLine("Zwei verschiedene Objekte");

Verweisen beide Referenzen auf dasselbe Objekt, liefert die Equals-Methode als Rückgabewert true. Das ist das Standardverhalten dieser Methode. In gleicher Weise arbeitet auch der ==-Vergleichsoperator. Wir könnten demnach die if-Anweisung auch wie folgt formulieren:


if(kreis1 == kreis2) 
  // Anweisungen

Die Instanzmethode Equals (es gibt auch eine statische Klassenmethode gleichen Namens in der Klasse Object) ist virtuell definiert und kann von jeder Klasse überschrieben werden, beispielsweise um anstatt eines Referenzvergleichs einen wertbasierten Vergleich der Zustandsdaten von zwei typgleichen Objekten zu erzwingen. Wenn Equals überschrieben wird, sollte auch der Vergleichsoperator der benutzerdefinierten Klasse überschrieben werden, damit die Austauschbarkeit zwischen Equals und dem ==-Operator weiterhin gewährleistet bleibt.

In GeometricObject wird nun die Equals-Methode so überschrieben, dass der Vergleich zweier Objekte nicht mehr anhand der Referenz erfolgt, sondern dazu die Fläche der Objekte ausgewertet wird.


public class GeometricObject {
  ...
  public static bool operator==(GeometricObject geo1, GeometricObject geo2) 
  {
    return geo1.Equals(geo2);
  }
  public static bool operator!=(GeometricObject geo1, GeometricObject geo2) 
  {
    return !geo1.Equals(geo2);
  }
  // überschriebene Methode Equals
  public override bool Equals(object obj) {
    if(GetArea() == ((GeometricObject)obj).GetArea())
      return true;
    return false;
  }
}

Die Implementierung der beiden operator-Methoden ist denkbar einfach: Es wird nur die in der Klasse überschriebene Methode Equals aufgerufen, die die eigentliche Arbeit verrichtet.

Soweit scheint alles tadellos zu funktionieren, wenn wir zwei Objekte miteinander vergleichen. Eine böse Überraschung erleben wir aber, wenn wir mit


if (kreis1 == null)

oder


if (kreis1 != null)

ein Circle oder Rectangle auf null hin prüfen: Es wird eine Ausnahme ausgelöst, weil in der überschriebenen Methode Equals ein null-Wert übergeben wird, Equals aber ein konkretes Objekt voraussetzt.

Eine erste Idee, diesem Malheur zu begegnen könnte sein, mit


if (geo1 == null || geo2 == null)

eine Überprüfung vorzuschalten. Allerdings ist auch dieser Versuch zum Scheitern verurteilt, da er zu einer StackOverflowException führt – die Operatorüberladung ruft sich in einer Endlosschleife immer wieder selbst auf.

Die Lösung des Problems kann nur über einen Weg führen, bei dem eine Überprüfung ohne die beiden Operatoren == und != stattfindet. Hier bietet uns die statische Methode ReferenceEquals der Klasse Object eine Möglichkeit. Dieser werden zwei Referenzen übergeben, die auf Gleichheit hin geprüft werden.


public static bool ReferenceEquals(Object objA, Object objB)

Mit Hilfe dieser Methode prüfen wir zuerst bei beiden Operanden, ob es sich um gültige Objekte oder null-Werte handelt. Erst wenn sichergestellt ist, dass beides konkrete Objekte sind, wird die Equals-Methode aufgerufen.


// endgültige Fassung
public static bool operator ==(GeometricObject geo1, GeometricObject geo2) {
  if (ReferenceEquals(geo1, null)) return true;
  if (ReferenceEquals(geo2, null)) return false;
  return geo1.Equals(geo2);  
}
public static bool operator !=(GeometricObject geo1, GeometricObject geo2) { 
  if (ReferenceEquals(geo1, null)) return false;
  if (ReferenceEquals(geo2, null)) return true;
  return !geo1.Equals(geo2);
}


public static bool operator <(GeometricObject geoObj1, 
GeometricObject geoObj2) {
if (geoObj1.GetArea() < geoObj2.GetArea())
return true;
return false;
}

Überladene Operatoren in der Vererbung

Wird aus einer Klasse, die Operatoren überlädt, eine weitere Klasse abgeleitet, vererben sich die überladenen Operatoren an die abgeleitete Klasse. In unserem Beispielprojekt werden somit die Klassen Circle und Rectangle von den Operatorüberladungen profitieren können.


Galileo Computing - Zum Seitenanfang

7.3.3 »true«- und »false«-Operatoren Zur nächsten ÜberschriftZur vorigen Überschrift

Wenn Sie Tabelle 7.3 aufmerksam studiert haben, werden Ihnen vielleicht zwei ungewöhnlich erscheinende, überladungsfähige Operatoren aufgefallen sein: true und false. Diese dienen dazu, Operationen wie beispielsweise


if (myObject)
  ...

zu ermöglichen. Diese Bedingungsprüfung ist sinnvoll, wenn der Rückgabewert direkt von einem Feld abhängt. Soll außerdem auch noch der Negationsoperator berücksichtigt werden, muss auch der !-Operator überladen werden.


if(!myObject)
  ...

Die Operatoren true und false gehören ebenfalls zu der Gruppe der Operatoren, die man nur paarweise überladen kann. Die Rückgabe ist ein boolescher Wert. Im folgenden Beispiel wird die Überladung aller drei Operatoren gezeigt. Dazu wird festgelegt, dass ein Objekt dann als true zu bewerten ist, wenn der Inhalt des objektspezifischen Felds ungleich 0 ist.


// ---------------------------------------------------------
// Beispiel: ...\Kapitel 7\True_False_Operatorüberladung
//----------------------------------------------------------
class Program {
  static void Main(string[] args) {
    Demo obj = new Demo();
    obj.Value = 8;
    if(obj)
      Console.Write("Wert ungleich 0");
    else
      Console.Write("Wert gleich 0");
    Console.ReadLine();
  }
}
// ---------------- Demo-Definition ------------------
class Demo {
  public int Value = 0;   
  // Überladung des true-Operators
  public static bool operator true(Demo obj) {
    if(obj.Value != 0)
      return true;
    return false;
  }      
  // Überladung des false-Operators
  public static bool operator false(Demo obj) {
    if(obj.Value != 0)
      return false;
    return true;
  }
  // Überladung des Negationsoperators
  public static bool operator !(Demo obj) {
    if(obj.Value != 0)
      return false;
    return true;
  }
}

Die dem Feld zugewiesene Zahl 8 wird mit


if (obj)

zu der Anzeige


Wert ungleich 0

führen. Benutzen wir im Ausdruck den !-Operator, kehrt sich die Logik um und führt zu folgender Ausgabe:


Wert gleich 0


Galileo Computing - Zum Seitenanfang

7.3.4 Benutzerdefinierte Konvertierungen topZur vorigen Überschrift

Implizite benutzerdefinierte Konvertierung

Stellen Sie sich vor, Sie hätten die Klasse DemoA folgendermaßen definiert:


class DemoA {
  public int Value = 0;
}

DemoA enthält nur ein int-Feld. Diese Definition könnte dazu verleiten, eine Referenz der Klasse DemoA einer int-Variablen wie folgt zuzuweisen:


DemoA obj = new DemoA();
obj.Value = 1;
int x = obj;

Selbstverständlich wird es nur bei einem Versuch bleiben, denn der Compiler stellt eine unzulässige Konvertierung des DemoA-Typs in einen int fest und macht das Unterfangen zunichte.

C# bietet uns die Möglichkeit, bestimmte Typkonvertierungen zu gestatten. Angenommen, unser Ziel sei, das Codefragment tatsächlich einwandfrei zu kompilieren. Dazu müssen wir die Klasse wie folgt um die Definition einer benutzerdefinierten Konvertierung erweitern:


class DemoA {
  public int Value = 0;
  public static implicit operator int(DemoA obj) {
   return obj.Value;
  }
}

Sehen wir uns den Methodenkopf genauer an. Im Vergleich zu einer Methode, die einen Operator überlädt, ist die Definition der Methode zur Typkonvertierung um das neue Schlüsselwort implicit ergänzt worden. Den Schlüsselwörtern implicit operator folgt der Datentyp, in den implizit konvertiert wird. In unserem Beispiel ist es int. Der Parameter definiert den Typ, der konvertiert werden soll.

Die allgemeine Syntax der impliziten benutzerdefinierten Typkonvertierung lautet:


public static implicit operator <Zieldatentyp>(<Eingabetyp>)

Die Aussage in unserem Beispiel ist also die folgende: Konvertiere ein Objekt vom Typ DemoA implizit in einen Integer.

Benutzerdefinierte Konvertierungen liefern ein Ergebnis: Es ist genau von dem Typ, der hinter operator angegeben wird. Im Anweisungsblock der Konvertierungsmethode muss deshalb ein return-Statement, gefolgt vom entsprechenden Rückgabewert, angegeben werden – in unserem Fall der Inhalt des Feldes Value des Objekts, dessen Referenz die Methode im Parameter obj empfängt.

Weil ein int vom System implizit in den Datentyp long konvertiert wird, wird jetzt auch das folgende Codefragment fehlerfrei kompiliert:


DemoA testObject = new DemoA();
testObject.Value = 1;
long x = testObject;

Benutzerdefinierte explizite Konvertierung

Eine implizite Konvertierung sollte nur in Betracht gezogen werden, wenn bei einer Konvertierung keine Daten verloren gehen. Nehmen wir nun an, die Klasse DemoA sei etwas anspruchsvoller:


class DemoA {
  public int Value = 0;
  public string Text = "Wohnort";
}

Wir wollen wieder sicherstellen, dass die Referenz auf ein DemoA-Objekt in einen Integer konvertiert werden kann. Dazu könnten wir auch hier eine implizite benutzerdefinierte Konvertierung anbieten. Tatsache ist aber, dass uns bei der Typumwandlung Informationen verloren gehen, auch wenn diese vom empfangenden Element nicht benötigt werden. Im Beispiel ist es das Feld vom Typ string. Wenn Sie Wert auf eine stilistisch saubere Programmierung legen, sollten Sie eine explizite Konvertierung vorschreiben. Sie vermeiden dadurch außerdem, dass eine implizite Konvertierung automatisch ausgeführt wird, ohne dass der Aufrufer sie gewünscht hat.

Um eine benutzerdefinierte, explizite Typumwandlung zu implementieren, müssen Sie das Schlüsselwort explicit in der Methodensignatur angeben. Die allgemeine Syntax ähnelt der der impliziten benutzerdefinierten Konvertierung:


public static explicit operator <Zieldatentyp>(<Eingabedatentyp>)

Sehen wir uns dazu das vollständige Beispiel an:


class DemoA {
  public int Value = 0;
  public string Text = "Explizite Konvertierung";
  public static explicit operator int(DemoA obj) {
    return obj.Value;
  }
  public static explicit operator string(DemoA obj) {
    return obj.Text;
  }
}

DemoA beschreibt nun sogar zwei explizite Konvertierungen: in einen int und in einen string. Programmcode, der ein DemoA-Objekt einem Integer zuweisen möchte, würde jetzt zu einer expliziten Konvertierung gezwungen, z. B. so:


DemoA testObject = new DemoA();
testObject.Value = 13;
int x = (int)testObject;

Analog lässt sich mit der Anweisung


string str = (string)testObject;

eine Referenz vom Typ DemoA auch einer string-Variablen zuweisen.

Implizite und explizite Konvertierung von Referenztypen

In den beiden vorhergehenden Abschnitten war der Zieldatentyp ein einfacher Datentyp. Auf dieselbe Art und Weise kann auch in einen Referenztyp konvertiert werden. Nehmen wir an, die beiden Klassen DemoA und DemoB wären wie folgt definiert:


class DemoA {
  public int Value;
  public bool Cancel;
}
class DemoB {
  public int Value;
  public string Text;
}

Unser Ziel sei uns nun, die implizite Konvertierung eines DemoA-Objekts in ein DemoB-Objekt und umgekehrt zu erreichen, um beispielsweise den folgenden Code zu ermöglichen:


DemoA objA = new DemoA();
objA.Value = 4722;
DemoB objB = objA;

Würden die beiden Klassen in einer Vererbungsbeziehung stehen und würde DemoA aus DemoB abgeleitet, wäre die Zuweisung kein Problem, da eine Subklassenreferenz einer Basisklassenreferenz zugewiesen werden kann. Dabei wird implizit konvertiert. Wie wir wissen, können wir auch die Referenz eines Basisklassenobjekts einer Subklassenreferenz zuweisen, müssen aber die Basisklassenreferenz dabei explizit konvertieren. Der passende Konvertierungsoperator wird uns aufgrund der Vererbungsbeziehung automatisch zur Verfügung gestellt. Die Ausgangssituation zwischen DemoA und DemoB ist in unserem Beispiel eine gänzlich andere und zwingt uns zu einer benutzerdefinierten Konvertierung, entweder implizit oder explizit.

Der Zieldatentyp ist nun DemoB, d. h., dass die Konvertierungsfunktion ein Objekt vom Typ DemoB als Rückgabewert liefert. In der Routine wird deshalb ein neues Objekt dieses Typs erzeugt. Man muss sich jetzt noch überlegen, welche gemeinsamen Merkmale beide Klassen aufweisen. In unserem Beispiel handelt es sich nur um das in beiden Klassen deklarierte Feld vom Typ int. Dass die beiden Felder namentlich identisch sind, ist hier nur rein zufällig so, es ist keine Voraussetzung.

Der Inhalt des Feldes Value des DemoA-Objekts wird dem int-Feld Value des DemoB-Objekts zugewiesen. Das so initialisierte Objekt wird als Ergebnis der impliziten Konvertierung bereitgestellt.


// implizite Konvertierung eines DemoA-Objekts in ein DemoB-Objekt
public static implicit operator DemoB(DemoA objA) {
  DemoB objB = new DemoB ();
  objB.Value = objA.Value;
  return objB;
}

Die Konvertierung eines DemoA-Objekts in ein DemoB-Objekt hat Datenverlust zur Folge. Daher ist es besser, anstelle der impliziten eine explizite Konvertierung vorzuschreiben. Dazu muss nur das Schlüsselwort implicit gegen explicit ausgetauscht werden:


// explizite Konvertierung eines DemoA-Objekts in ein DemoB-Objekt
public static explicit operator DemoB(DemoA objA) {
  DemoB objB = new DemoB ();
  objB.Value = objA.Value;
  return objB;
}

Nun wird die Typumwandlung nur über den Konvertierungsoperator ermöglicht, führt aber ansonsten zum gleichen Ergebnis wie eine implizite Konvertierung:


DemoA objA = new DemoA();
objA.Value = 55;
DemoB objB = (DemoB)objA;


Hinweis

Sie finden das komplette Beispielprojekt GeometricObjectsSolution auf der Buch-DVD unter \Kapitel7\GeometricObjectsSolution. Die Klasse GeometricObject ist hier auch noch um die Überladung der Operatoren < und > ergänzt worden, um die Vollständigkeit der Operatorüberladung sicherzustellen.

public static bool operator >(GeometricObject geoObj1, 
GeometricObject geoObj2) {
if (geoObj1.GetArea() > geoObj2.GetArea())
return true;
return false;
}
Hinweis

Auf der Buch-DVD finden Sie die Beispielprogramme ConvertImplicitDemo und ConvertExplicitDemo, die die implizite und explizite Konvertierung zeigen.


Überladen benutzerdefinierter Konvertierungsmethoden

Das gleichzeitige Implementieren einer expliziten und einer impliziten Konvertierungsfunktion mit demselben Zieldatentyp ist unzulässig. Daher ist der folgende Programmcode falsch:


// fehlerhafte Klassendefinition aufgrund unzulässiger 
// Überladung der benutzerdefinierten Konvertierung
class Demo {
  public int Value;
  public static implicit operator int(Demo obj) {
    return obj.Value;
  }
  public static explicit operator int(Demo obj) {
    return obj.Value;
  }
}

Gestattet ist jedoch die Koexistenz einer impliziten und einer expliziten Konvertierung, wenn sich die beiden Überladungsmethoden im Zieldatentyp unterscheiden. Im folgenden Codefragment ist in Demo eine explizite Konvertierung in den Typ long definiert und eine implizite in einen int.


// zulässige Überladung der Konvertierungsmethode  
class Demo {
  public int Value;
  public static implicit operator int(Demo obj) {
    return obj.Value;
  }
  public static explicit operator long(Demo obj) {
    return obj.Value;
  }
}



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