4.3 Typumwandlung und Typuntersuchung von Objektvariablen
4.3.1 Die implizite Typumwandlung von Objektreferenzen
Die Klasse Luftfahrzeug beschreibt Eigenschaften und Operationen, die allen Luftfahrzeugen, unabhängig vom Typ, eigen sind. Die Klassen Flugzeug und Hubschrauber beerben als abgeleitete Klassen die Basisklasse.
Betrachten wir einen Ausschnitt der Klassenhierarchie, nämlich die beiden Klassen Flugzeug und Luftfahrzeug. Wenn wir unsere Erkenntnisse aus der realen Welt auf unseren Code projizieren, kommen wir zu der Aussage, dass ein Flugzeug ein Luftfahrzeug ist. Andererseits ist aber ein Luftfahrzeug nicht zwangsläufig ein Flugzeug, denn es könnte sich auch um einen Hubschrauber handeln. Die Tatsache, dass das Objekt einer abgeleiteten Klasse (hier Flugzeug) 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, denn aufgrund dieser Beziehung kann man die Referenz eines Subklassenobjekts einer Basisklassenreferenz zuweisen:
Flugzeug flg = new Flugzeug();
Luftfahrzeug lfzg = flg;
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 flg und lfzg referenzieren denselben Speicherbereich – jedoch mit einer kleinen Einschränkung: Die Laufzeitumgebung betrachtet lfzg nur als Objekt vom Typ Luftfahrzeug und nicht als Flugzeug. Damit hat die Objektreferenz lfzg auch keinen Zugriff auf die Member, durch die sich ein Objekt vom Typ Flugzeug 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 rechts vom Zuweisungsoperator stehenden Referenz haben. Betrachten Sie dazu die Abbildung 4.5, die diesen Sachverhalt veranschaulicht. Dass dabei das Feld Spannweite einer Flugzeug-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 DoSomething(Luftfahrzeug lfzg) {
[...]
}
Die Methode DoSomething erwartet vom Aufrufer die Referenz auf ein Luftfahrzeug. Ob es sich dabei um ein Objekt vom Typ Hubschrauber oder Flugzeug handelt, spielt keine Rolle. Ausschlaggebend ist ausschließlich, dass der Typ der übergebenen Referenz vom Typ Luftfahrzeug oder davon abgeleitet ist. Flugzeug erfüllt diese Bedingung. Daher kann die Methode DoSomething folgendermaßen aufgerufen werden:
Flugzeug flg = new Flugzeug();
@object.DoSomething(flg);
Parameter vom Typ einer Basisklasse werden häufig dann eingesetzt, wenn unabhängig vom genauen Typ innerhalb der Methode auf einen in der Basisklasse definierten Member zugegriffen wird. Beispielsweise könnte man sich vorstellen, dass in DoSomething die Methode Starten des übergebenen Objekts aufgerufen wird:
public void DoSomething(Luftfahrzeug lfzg) {
[...]
lfzg.Starten();
[...]
}
Da sowohl ein Flugzeug- als auch ein Hubschrauber-Objekt über diese Methode verfügt, ist DoSomething eine hinsichtlich der Luftfahrzeuge allgemein gehaltene Methode. Das erspart Ihnen, zwei verschiedene Methoden DoSomething 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 DoSomething bei einer späteren Erweiterung der Vererbungshierarchie, beispielsweise durch eine Klasse Rocket, auch mit einem Objekt vom Typ Rocket einwandfrei funktioniert.
4.3.2 Die explizite Typumwandlung von Objektreferenzen
Wenn es erforderlich ist, können Sie auch eine Basisklassenreferenz in eine Subklassenreferenz konvertieren. Also:
Flugzeug flg = new Flugzeug();
Luftfahrzeug lfzg = flg;
[...]
Flugzeug flugzeug = (Flugzeug)lfzg;
Listing 4.13 Explizite Typkonvertierung
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 Ausgangs- und Zieldatentyp in einer Vererbungsbeziehung befinden.
Die explizite Konvertierung innerhalb einer Vererbungshierarchie auf horizontaler Ebene in einer Klassenhierarchie, beispielsweise vom Typ Flugzeug in den Typ Hubschrauber, ist nicht gestattet.
4.3.3 Typuntersuchung mit dem »is«-Operator
Manchmal ist es notwendig, den sich hinter einer Basisklassenreferenz verbergenden Typ festzustellen, beispielsweise wenn ein typspezifischer 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 DoSomething soll in Abhängigkeit vom übergebenen Typ entweder die Spannweite oder der Rotordurchmesser ausgegeben werden. Wir müssen dann die Methode wie nachfolgend gezeigt ergänzen:
public void DoSomething(Luftfahrzeug lfzg) {
if (lfzg != null)
{
if (lfzg is Flugzeug)
Console.WriteLine("Spannweite: ", ((Flugzeug)lfzg).Spannweite);
else if (lfzg is Hubschrauber)
Console.WriteLine("Rotor: ", ((Hubschrauber)lfzg).Rotor);
else
Console.WriteLine("Unbekannter Typ.");
}
}
Listing 4.14 Typuntersuchung mit dem Operator »is«
In der Methode wird der Parameter lfzg zwei Ü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, z. B.:
((Flugzeug)lfzg).Spannweite
Der Ausdruck (Flugzeug)lfzg ist in runden Klammern gesetzt, um eine Typkonvertierung vor dem Aufruf der Eigenschaft zu erzwingen. Grund dafür ist, dass der Punktoperator eine höhere Priorität besitzt als der Konvertierungsoperator. Nach der zusätzlichen Klammerung bezieht der Punktoperator 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, 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.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.