4.3 Typumwandlung und Typuntersuchung von Objektvariablen 

4.3.1 Implizite Typumwandlung von Objektreferenzen 

Die Klasse Luftfahrzeug beschreibt Eigenschaften und Operationen, die allen Luftfahrzeugen, unabhängig vom Typ, eigen sind. Die Klassen Zeppelin, Flugzeug und Hubschrauber beerben als abgeleitete Klassen die Basisklasse.
Betrachten wir einen Ausschnitt der Klassenhierarchie, nämlich die beiden Klassen Zeppelin und Luftfahrzeug. Wenn wir unsere Erkenntnisse aus der realen Welt auf unseren Code projizieren, kommen wir zu der Aussage, dass ein Zeppelin ein Luftfahrzeug ist. Andererseits ist aber ein Luftfahrzeug nicht zwangsläufig ein Zeppelin, denn es könnte sich auch um ein Flugzeug oder einen Hubschrauber handeln. Die Tatsache, dass das Objekt einer abgeleiteten Klasse (hier Zeppelin) auch gleichzeitig ein Objekt der Basisklasse (hier Luftfahrzeug) ist, wird als Ist-ein(e)-Beziehung bezeichnet. Diese Aussage ist nicht neu, sie wurde bereits am Anfang dieses Kapitels gemacht.
Die Vererbung hat Konsequenzen hinsichtlich der Zuweisungsoperationen, denn aufgrund dieser Beziehung kann man die Referenz eines Subklassenobjekts einer Basisklassenreferenz zuweisen:
Zeppelin zpln = new Zeppelin(); Luftfahrzeug lfzg = zpln;
Stehen zwei Klassen miteinander in einer Vererbungsbeziehung, kann eine Referenz vom Typ der abgeleiteten Klasse der Referenz vom Typ einer der Basisklassen mit
Basisklassenreferenz = Subklassenreferenz
zugewiesen werden. Dabei wird implizit konvertiert.
Die beiden Variablen zpln und lfzg referenzieren denselben Speicherbereich – jedoch mit einer kleinen Einschränkung: Die Laufzeitumgebung betrachtet lfzg nur als Objekt vom Typ Luftfahrzeug und nicht als Zeppelin. Damit hat die Objektreferenz lfzg auch keinen Zugriff auf die Member, durch die sich ein Objekt vom Typ Zeppelin auszeichnet.
Bei einer Zuweisung einer Subklassenreferenz an eine Basisklassenreferenz müssen alle Member der links vom Zuweisungsoperator angegebenen Referenz einen konkreten Bezug zu einem Mitglied der Referenz haben, die rechts vom Zuweisungsoperator steht. Betrachten Sie dazu Abbildung 4.5, die diesen Sachverhalt veranschaulicht. Dass dabei das Feld Gasvolumen einer Zeppelin-Referenz keinen Abnehmer in der Luftfahrzeug-Referenz findet, spielt keine Rolle.
Abbildung 4.5 Zuweisung einer Subklassenreferenz an eine Basisklassenreferenz
Die Tatsache, dass ein Objekt vom Typ einer abgeleiteten Klasse auch gleichzeitig ein Objekt vom Typ seiner Basisklasse ist, kann man sich bei der Typfestlegung eines Parameters zunutze machen:
public void TestProc(Luftfahrzeug lfzg) {
...
}
Die Methode TestProc erwartet vom Aufrufer die Referenz auf ein Luftfahrzeug. Ob es sich dabei um ein Objekt vom Typ Zeppelin, Hubschrauber oder Flugzeug handelt, spielt keine Rolle. Ausschlaggebend ist ausschließlich, dass der Typ der übergebenen Referenz von Luftfahrzeug abgeleitet ist. Zeppelin erfüllt diese Bedingung. Daher kann die Methode TestProc folgendermaßen aufgerufen werden:
Zeppelin zpln = new Zeppelin(); IrgendEinObjekt.TestProc(zpln);
Parameter vom Typ einer Basisklasse werden häufig dann eingesetzt, wenn unabhängig vom genauen Typ innerhalb der Methode auf ein in der Basisklasse definiertes Member zugegriffen wird. Beispielsweise könnte man sich vorstellen, dass in TestProc die Methode Starten des übergebenen Objekts aufgerufen wird:
public void TestProc(Luftfahrzeug lfzg) { ... lfzg.Starten(); ... }
Da sowohl ein Flugzeug- als auch ein Zeppelin- oder Hubschrauber-Objekt über diese Methode verfügt, ist diese TestProc eine hinsichtlich der Luftfahrzeuge allgemein gehaltene Methode. Das erspart Ihnen, drei verschiedene Methoden TestProc zur Verfügung zu stellen. Denn genau das müssten Sie machen, gäbe es die implizite Konvertierung und Vererbung nicht. Zudem ist auch sichergestellt, dass die Methode TestProc bei einer späteren Erweiterung der Vererbungshierarchie, beispielsweise durch eine Klasse Rakete, auch mit einem Objekt vom Typ Rakete einwandfrei funktioniert.
4.3.2 Explizite Typumwandlung von Objektreferenzen 

Wenn es erforderlich ist, können Sie auch eine Basisklassenreferenz in eine Subklassenreferenz konvertieren. Also:
Zeppelin zep = new Zeppelin(); Luftfahrzeug lfzg = zep; ... Zeppelin flg = (Zeppelin)lfzg;
Bei der expliziten Typumwandlung gilt die folgende Regel:
Subklassenreferenz = (Zieldatentyp)Basisklassenreferenz
Den Zieldatentyp geben Sie in runden Klammern vor der umzuwandelnden Referenz an. Der Erfolg der Typumwandlung setzt allerdings voraus, dass vorher eine implizite Konvertierung des Subklassentyps in den Typ der Basisklasse stattgefunden hat. Die explizite Konvertierung ist demnach die Umkehrung der impliziten Konvertierung, die nur dann erfolgt, wenn sich die Ausgangs- und Zieldatentypen in einer Vererbungsbeziehung befinden.
Die explizite Konvertierung innerhalb einer Vererbungshierarchie auf horizontaler Ebene in einer Klassenhierarchie, beispielsweise eine Konvertierung vom Typ Flugzeug in den Typ Hubschrauber, ist nicht gestattet.
4.3.3 Typuntersuchung mit dem is-Operator 

Manchmal ist es notwendig, den Typ festzustellen, der sich hinter einer Basisklassenreferenz verbirgt, beispielsweise dann, wenn in Abhängigkeit vom tatsächlichen Typ bestimmte Operationen ausgeführt werden sollen oder ein typspezifisches Member aufgerufen werden soll. Zur Lösung dieser Aufgabe bietet uns C# den is-Operator an.
Sehen wir uns dazu ein konkretes Beispiel an, und nehmen wir an, in der Methode TestProc soll in Abhängigkeit vom übergegebenen Typ entweder die Spannweite, das Gasvolumen oder der Rotordurchmesser ausgegeben werden. Wir müssen dann die Methode wie folgt ergänzen:
public void TestProc(Luftfahrzeug lfzg) { if (lfzg != null) { if (lfzg is Flugzeug) Console.WriteLine("Spannweite: ", ((Flugzeug)lfzg).Spannweite); else if (lfzg is Zeppelin) Console.WriteLine("Gasvolumen: ", ((Zeppelin)lfzg).Gasvolumen); else if (lfzg is Hubschrauber) Console.WriteLine("Rotor: ", ((Hubschrauber)lfzg).RotorDurchmesser); else Console.WriteLine("Unbekannter Typ."); } }
In der Methode wird der Parameter lfzg drei Überprüfungen unterzogen. Dabei steht links vom is-Operator die zu überprüfende Referenz, rechts davon der Typ, auf den hin die Referenz geprüft werden soll. Der Vergleich liefert true, wenn der Ausdruck, also die Referenz, in den rechts von is stehenden Typ umgewandelt werden kann.
Da der Methodenaufruf auch dann richtig ist, wenn dem Parameter null übergeben wird, sollte der Parameter als Erstes mit
if (lfzg != null)
daraufhin untersucht werden, ob er auch tatsächlich ein konkretes Objekt beschreibt.
Beachten Sie im Codefragment auch die Konsolenausgabe, zum Beispiel:
((Flugzeug)lfzg).Spannweite
Der Ausdruck (Flugzeug)lfzg wurde in runde Klammern gesetzt, um eine Typkonvertierung vor dem Aufruf der Eigenschaft zu erzwingen. Der Grund dafür ist, dass der Punktoperator eine höhere Priorität besitzt als der Konvertierungsoperator. Nach der zusätzlichen Klammerung bezieht der Punktoperator dann seine Informationen aus dem Zieldatentyp der Umwandlung.
4.3.4 Typumwandlung mit dem as-Operator 

Eine Referenz kann mit dem ()-Konvertierungsoperator in einen anderen Typ konvertiert werden, wenn vorher eine implizite Konvertierung stattgefunden hat. Beispielsweise kann eine Instanz der Klasse Luftfahrzeug in den Typ Flugzeug konvertiert werden:
Flugzeug flg = (Flugzeug)lfzg;
C# bietet mit dem as-Operator noch eine weitere Konvertierungsvariante an:
Flugzeug flg = lfzg as Flugzeug;
Das Ergebnis ist dasselbe – wenn sich hinter der Referenz lfzg auch tatsächlich eine Flugzeug-Referenz verbirgt. Beide Möglichkeiten, also der Konvertierungs- und der as-Operator, verhalten sich aber unterschiedlich, wenn die Basisklassenreferenz keine Flugzeug-, sondern beispielsweise eine Hubschrauber-Referenz beschreibt:
- Die Typumwandlung mit dem Konvertierungsoperator löst eine Exception (Ausnahme) aus, wenn die Konvertierung scheitert.
- Der as-Operator liefert als Ergebnis null.
Der as-Operator bietet sich daher auch in einem if-Statement als Bedingung an:
if(lfzg as Flugzeug != null) ...
Beachten Sie, dass der as-Operator nur im Zusammenhang mit Referenztypen genutzt werden kann.