Der Basistyp von Schnittstellentypen
Einleitung
Während in C# Verweise eines Schnittstellentyps direkten Zugriff auf Mitglieder des Typs System.Object
gewähren, kann darauf in Visual Basic .NET nur über Verweise vom Typ einer Klasse zugegriffen werden. Dieser Artikel geht der Frage nach, ob Schnittstellen vom Typ Object
erben und wie Spezifikationen und Implementierungen der CLR und der Programmiersprachen Visual Basic .NET und C# dies handhaben.
Verhalten von Visual Basic .NET und C#
Der Unterschied zwischen Visual Basic .NET und C# hinsichtlich des Verhaltens beim Aufruf von Methoden der Klasse Object
an Schnittstellenverweisen soll anhand eines gegenüberstellenden Beispiels demonstriert werden. Folgendes Listing zeigt äquivalente Codebeispiele in Visual Basic .NET und C#, bestehend aus Definitionen der Schnittstelle IBar
, der Klasse FooBar
, welche die Schnittstelle IBar
implementiert, und einer Variablen iref
vom Typ IBar
, die auf eine Instanz der Klasse FooBar
verweist. Im Anschluß daran wird die Methode ToString
der Variablen iref
aufgerufen und deren Rückgabewert einer weiteren Variablen zugewiesen. Während der C#-Code kompilierbar ist, führt der Versuch, den Visual-Basic-.NET-Code zu kompilieren, zum Compilerfehler BC30456 (ToString
ist kein Member von IBar
).
Um den Visual-Basic-.NET-Code kompilierbar zu machen, muß bar
vor Aufruf der Methode ToString
in einen Typ umgewandelt werden, der von Object
erbt. Dies kann über die Operatoren CObj
, DirectCast
, CType
oder durch Zuweisen an eine Variable des Typs Object
oder eines anderen Basistyps von FooBar
erfolgen. Dabei ist unerheblich, ob es sich dabei um einen Typ handelt, von dem geerbt werden muß, jedoch existiert nicht immer ein derartiger Typ. Eine Umwandlung in den Typ FooBar
funktioniert zwar, stellt aber die Verwendung einer Variablen des Basisschnittstellentyps IBar
in Frage. Das folgende Listing demonstriert die beschriebenen Vorgehensweisen am Beispiel aus dem vorhergehenden Listing.
Alle instanzierbaren Typen erben von Object
und Verweise mit Schnittstellentypen verweisen immer auf Typinstanzen oder enthalten einen Nullverweis, also einen Verweis auf Nothing
. Daraus folgt, daß jedes Objekt, dessen Methoden über einen Verweis eines Schnittstellentyps aufgerufen werden kann, über die Methoden des Typs Object
verfügt. Aus technischer Sicht spricht demnach nichts dagegen, Zugriffe auf Objektmitglieder der Klasse Object
über einen Verweis mit Schnittstellentyp ohne eine explizite Typumwandlung zu erlauben. An dieser Stelle fragt sich vielleicht der eine oder andere Leser, warum die Zuweisung von iref
an oref
im dritten Beispiel nicht zu einem Kompilierungsfehler führt. Der folgende Abschnitt über erweiternde Typumwandlungen gibt Antwort auf diese Frage.
Erweiternde Typumwandlungen in Visual Basic .NET
Der Grund für die Kompilierbarkeit des Beispiels 3 aus dem vorhergehenden Listing liegt darin, daß Visual Basic .NET eine erweiternde implizite Typumwandlung von jedem Typ in den Typ Object
unterstützt. Unter einer erweiternden Typumwandlung versteht man eine implizite verlustfreie Umwandlung; der Zieltyp kann dabei jeden Wert des Ausgangstyps annehmen. Im Folgenden wird ein Auszug aus der aus Visual Basic Language Concepts – Widening and Narrowing Conversions entnommenen Tabelle der in Visual Basic .NET unterstützten erweiternden Typumwandlungen wiedergegeben.
Data type
Widens to data types
[…]
[…]
Char
Char
, String
[…]
[…]
Any type
Object
Any type
Any base type from which it is derived
[…]
[…]
[…]
Widening conversions always succeed and can always be performed implicitly.
Implizite Typumwandlungen können bei Zuweisungen oder Vergleichen stattfinden, nicht jedoch direkt an einem Verweis durch beispielsweise einen Methodenaufruf. Anders ausgedrückt bedeutet dies, daß eine Variable des Typs Char
zwar einer Variablen des Typs String
ohne explizite Typumwandlung zugewiesen werden kann, eine Variable des Typs Char
jedoch nicht über die Methoden des Typs String
verfügt. Da jeder Typ in den Datentyp Object
erweitert werden kann, ist keine explizite Typumwandlung beim Zuweisen von iref
an oref
erforderlich. Ein direkter Zugriff auf Mitglieder des Typs Object
ist allerdings nicht möglich, da erweiternde Umwandlungen nicht direkt an Verweisen bei Mitgliedszugriff stattfinden können. Der im nachstehenden Listing angegebene Code ist demnach nicht kompilierbar, obwohl Length
eine Methode von String
ist und Char
zu String
erweitert werden kann.
Ähnlich verhält es sich bei Schnittstellentypen und deren Erweiterung zum Typ Object
. Die Erweiterung findet nicht statt, wenn eine Methode einer Variablen aufgerufen wird, deren Typ diese Methode nicht besitzt. Würden erweiternde Typumwandlungen auch direkt an einem Verweis stattfinden, käme es schnell zu Mehrdeutigkeiten, wenn mehrere Typen, in die erweitert werden kann, gleichnamige Methoden besitzen. Zudem wäre es unintuitiv und semantisch inkorrekt, wenn beispielsweise Mitglieder der Klasse String
direkt über eine Variable des Typs Char
zugänglich wären. Dieses Verhalten gilt auch für gemeinsame Mitglieder, wie im vorigen Beispiel an der Methode Double.TryParse
gezeigt wurde. In den folgenden beiden Listings wird das Verhalten von Visual Basic .NET in Bezug auf erweiternde Typumwandlungen demonstriert. Nachstehendes Beispiel zeigt, unter welchen Bedingungen erweiternde implizite Typumwandlungen stattfinden.
Die erweiternde Typumwandlung zwischen abgeleitetem Typ und einem der Basistypen stellt den Trivialfall dar, trifft aber nicht auf die Umwandlung zwischen Schnittstellentypen und dem Typ Object
zu. Daraus folgt, daß Object
nicht Basistyp von Schnittstellentypen ist. Folgendes Listing zeigt ein weiteres Beispiel, dem zu entnehmen ist, unter welchen Umständen implizite erweiternde Typumwandlungen vorgenommen werden.
Ist Object
ein Basistyp von Schnittstellen?
Zur Klärung der Frage, welche der beiden Programmiersprachen Visual Basic .NET und C# sich in seinem Umgang mit Verweisen von Schnittstellentypen näher an die Vorgaben von .NET hält, sollen in den folgenden Abschnitten Dokumentation und Implementierung von CLR, Visual Basic .NET und C# in dieser Hinsicht näher betrachtet werden. Sofern nicht weiter angegeben, wurden die mit Visual Studio .NET 2003 mitgelieferte Version der Dokumentation und darin enthaltene Compiler und Bibliotheken zur Auswertung benutzt.
Die nachstehende Tabelle gibt einen Überblick über die Ergebnisse dieser Untersuchung. Wenn man alle berücksichtigen Ausschnitte aus Dokumentation und den Implementierungen für eine Einschätzung heranzieht, kann festgestellt werden, daß sich Visual Basic .NET mehrheitlich an die im .NET Framework und der CLR vorgegebene Semantik hält und C# dieser mehrheitlich widerspricht. Innerhalb keiner der drei Gruppen bietet sich kein konsistentes Bild, in dem Dokumentation und Implementierung einander nicht widersprechen. Es ist zu beachten, daß die Anzahl der Kreuzchen in der Tabelle für oder wider der Ansicht, daß Object
Basistyp von Schnittstellentypen ist, keine quantitative, allgemein gültige Aussage zulassen, da die Liste keinesfalls den Anspruch der Vollständigkeit erhebt.
Object ist Basistyp von Schnittstellentypen |
|||
---|---|---|---|
.NET Framework und CLR | Eigenschaft Type.BaseType |
Implementierung | Nein |
Dokumentation | Ja | ||
Kommentar in der SSCLI | Nein | ||
Implementierung der Methode Type.GetMethods |
Nein | ||
Implementierung der Methode Type.GetMethod |
Nein | ||
Visual Basic .NET | Aufruf an Schnittstellenverweis | Nein | |
Objektbrowser | Nein | ||
Sprachspezifikation | Kap. 7.8 | Nein | |
Kap. 7.1 | Ja | ||
C# | Aufruf an Schnittstellenverweis | Ja | |
Objektbrowser | Ja | ||
Sprachspezifikation | Kap. 1.2.1 | Ja | |
Kap. 7.3.1 | Ja |
.NET Framework und CLR
Dokumentation zur Eigenschaft Type.BaseType
Aus .NET Framework Class Library – Type.BaseType
Property:
➞ Object
ist Basistyp von Schnittstellentypen.
Implementierung der Eigenschaft Type.BaseType
Die Dokumentation der Eigenschaft Type.BaseType
steht im Widerspruch ihrer Implementierung im .NET Framework. Für Schnittstellen, die keine weiteren Schnittstellen erben, gibt BaseType
einen Nullverweis (Nothing
) zurück, bei einem Typ, der die Schnittstelle implementiert und direkt von Object
erbt, wird Object
zurückgegeben. BaseType
gibt, wie nicht anders zu erwarten, unabhängig von der aufrufenden Programmiersprache den selben Wert zurück.
➞ Object
ist nicht Basistyp von Schnittstellentypen.
Kommentar in der SSCLI bei der Eigenschaft Type.BaseType
Das Verhalten von Type.BaseType
, im Falle des Aufrufs für einen Schnittstellentyp einen Nullverweis zurückzugeben, wird auch in einem Kommentar in der SSCLI-Implementierung vermerkt.
Aus Rotor Source Code – System.Type
:
➞ Object
ist nicht Basistyp von Schnittstellentypen.
Implementierung der Methode Type.GetMethods
Die Methode Type.GetMethods
dient dazu, zur Laufzeit Mitglieder eines Typs zu ermitteln. Das von der Methode GetMethods
zurückgegebene Array von MethodInfo
-Objekten ist leer. Der nach Visual Basic .NET übertragene Code aus dem nächsten Listing würde ebenfalls ein leeres Array zurückliefern, da in beiden Fällen die selbe Methode aufgerufen wird. Die im Beispielcode verwendete Kombination von BindingFlags
müßte, sollte der Schnittstellentyp von Object
erben, auch dessen Mitglieder zurückgeben. Gleich verhält es sich bei der Methode Type.GetMethod
, mit der Informationen zu einer einzelnen Methode bestimmt werden können. In der Dokumentation zu den beiden Methoden wird keine Aussage bezüglich des Verhaltens bei Abruf von Mitgliedern des Typs Object
für Schnittstellentypen getroffen.
➞ Object
ist nicht Basistyp von Schnittstellentypen.
Visual Basic .NET
Zugriff auf Mitglieder der Klasse Object
an Verweisen eines Schnittstellentyps
Wie gezeigt wurde, unterstützt Visual Basic .NET nicht den Zugriff auf Mitglieder der Klasse Objekt an Verweisen eines Schnittstellentyps. Das nächste Listing zeigt zur Erinnerung das nicht kompilierbare Codebeispiel.
➞ Object
ist nicht Basistyp von Schnittstellentypen.
Kapitel 7.1 Value Types and Reference Types der Sprachspezifikation
Aus Visual Basic Language Specification – 7.1 Value Types and Reference Types:
➞ Object
ist Basistyp von Schnittstellentypen.
Kapitel 7.8 Interfaces der Sprachspezifikation
Im Abschnitt Visual Basic Language Specification – 7.8 Interfaces werden zwei Aussagen zu Basistypen von Schnittstellen getroffen:
Aus Abschnitt 7.8.1:
Aus Abschnitt 7.8.2:
Das bedeutet, daß Schnittstellentypen nicht, auch nicht implizit, vom Typ Object
erben.
➞ Object
ist nicht Basistyp von Schnittstellentypen.
Der Operator TypeOf…Is
In Visual Basic .NET kann der Operator TypeOf…Is
benutzt werden, um zu überprüfen, ob der Laufzeittyp eines Wertes kompatibel zu einem bestimmten Typ ist. Der Rückgabewert des Operators ist True
, wenn der Laufzeittyp des Operanden vom Typ erbt oder den Typ implementiert. Die Implementierung von TypeOf…Is
entspricht diesem in Abschnitt 11.5.2 TypeOf…Is
Expressions der Sprachspezifikation angegebenen Verhalten. Es gilt zu beachten, daß TypeOf…Is
nicht den Typ einer Variablen, sondern den Laufzeittyp des Objekts, auf welches sie verweist, überprüft. Enthält die Variable einen Nullverweis, wird immer False
zurückgegeben.
➞ Keine Aussage, da der Laufzeittyp einer Klasseninstanz geprüft wird.
Objektbrowser in Visual Basic .NET 2003
Der Objektbrowser von Visual Basic .NET 2003 führt Object
nicht als Basistyp von Schnittstellen auf, unabhängig davon, ob die Anzeige erweiterter Mitglieder aktiviert ist oder nicht. Die folgende Abbildung zeigt das Aussehen des Objektbrowsers mit Auswahl einer Schnittstelle. Der Objektbrowser entspricht dem in Visual Basic .NET mehrheitlich vorzufindenden Verhalten.
➞ Object
ist nicht Basistyp von Schnittstellentypen.
C#
Zugriff auf Mitglieder des Typs Object
an Verweisen eines Schnittstellentyps
C# erlaubt den Zugriff auf Mitglieder des Typs Object
an Verweisen eines Schnittstellentyps. Der im nachstehenden Listing gezeigte Code illustriert dieses Verhalten, das im Widerspruch zu den von Reflection zurückgegebenen Informationen steht. Der Grund für die Zulässigkeit des untenstehenden Codes dürfte darin zu finden sein, daß zur Laufzeit ein Verweis eines Schnittstellentyps entweder einen Nullverweis darstellt oder aber auf eine Instanz eines die Schnittstelle implementierenden, instanzierbaren Typs verweist, sodaß kein Unterschied zu Verweisen eines instanzierbaren Typs vorliegt.
➞ Object
ist Basistyp von Schnittstellentypen.
Kapitel 1.2.1 Predefined types der Sprachspezifikation
Aus C# Language Specification – 1.2.1 Predefined types:
➞ Object
ist Basistyp von Schnittstellentypen.
Kapitel 7.3.1 Base types der Sprachspezifikation
Aus C# Language Specification – 7.3.1 Base types:
➞ Object
ist Basistyp von Schnittstellentypen.
Der Operator is
Der Operator is
in C# entspricht dem Operator TypeOf…Is
von Visual Basic .NET, weshalb auf eine genauere Beschreibung verzichtet wird. Dennoch ist das Verhalten des C#-Compilers in Bezug auf den is
-Operator beachtenswert. Betrachten wir dazu den im folgenden Listing wiedergegebenen Code. Der Versuch, den Code zu kompilieren, führt zur Compilerwarnung CS0183 (Der Ausdruck ist immer vom bereitgestellten (object
) Typ). Das Auftreten dieser Warnung steht in Einklang zur Sprachspezifikation. Allerdings evaluiert der Ausdruck iref is object
bei Ausführung des Codestücks zu False
, wenn iref
einen Nullverweis enthält. Der Compiler überprüft anscheinend den Typ der Variablen zur Kompilierungszeit, obwohl der Operator is
zur Überprüfung des Laufzeittyps vorgesehen ist. Im Compiler für C# 2.0 ist das Problem behoben, es wird keine Warnung mehr angezeigt.
Aus C# Programmer’s Reference – is
:
➞ Keine Aussage, da der Laufzeittyp einer Klasseninstanz geprüft wird.
Objektbrowser in Visual C# 2003
Der Objektbrowser von C# führt Object
als Basistyp von Schnittstellentypen an. In der folgenden Abbildung ist das Aussehen des Objektbrowsers von C# für einen Schnittstellentyp und dessen Basisklassen und Schnittstellen zu sehen. Die Darstellung entspricht der Sicht, die in IntelliSense geboten und vom C#-Compiler zugelassen wird.
➞ Object
ist Basistyp von Schnittstellentypen.
Herstellen eines konsistenten Verhaltens
Im Folgenden sollen drei Möglichkeiten der Vereinheitlichung des Verhaltens der Programmiersprachen Visual Basic .NET und C# sowie des .NET Frameworks in Bezug auf Schnittstellen und deren Basistypen zur Herstellung konsistenten Verhaltens vorgestellt werden.
-
Was passiert, wenn
System.Reflection
undSystem.Type
dahingehend angepaßt werden, daßObject
als Basistyp von Schnittstellen angesehen würde?Eine derartige Änderung ist problematisch, da ihre Folgen schwer abzuschätzen sind. Bestehender Code, der vom momentanen Verhalten von
System.Reflection
undSystem.Type
Gebrauch macht, würde unerwartete Ergebnisse liefern. -
Was würde passieren, wenn Visual Basic .NET Zugriff auf Mitglieder des Typs
Object
an Verweisen eines Schnittstellentyps zulassen würde?Aus technischer Sicht würden sich daraus keine Probleme ergeben. Alle Visual-Basic-.NET-Anwendungen, die momentan Methoden der Klasse
Object
über Schnittstellenverweise aufrufen, müssen sich dazu einer expliziten Typumwandlung in den TypObject
bedienen oder den Schnittstellenverweis einem Klassenverweis zuweisen bzw. an diesen übergeben. Existierende Programme würden durch die Änderung ihr Verhalten nicht verändern, es würde lediglich eine alternative vereinfachte Syntax geboten.Da zur Laufzeit Schnittstellenverweise entweder auf
Nothing
verweisen oder auf eine Instanz vonObject
oder eines davon abgeleiteten Klassentyps, würde der Zugriff auf Methoden der KlasseObject
semantisch sinnvoll sein. Jeder Instanz ist Instanz eines instanzierbaren Typs und die Basisklasse aller instanzierbaren Typen istObject
, deshalb kann es auch nicht vorkommen, daß Methoden des Objekts, auf das verwiesen wird, aufgerufen werden, über die dieses nicht verfügt. -
Was würde passieren, wenn die Programmiersprache C# dahingehend angepaßt werden würde, Zugriffe auf Mitglieder des Typs
Object
an Verweisen eines Schnittstellentyps zu verbieten?Eine Angleichung des Verhaltens von C# an jenes der Visual-Basic-.NET-Implementierung und des .NET Frameworks wäre praktisch nicht rechtfertigbar und durchführbar, da eine Menge bestehenden Codes inkompatibel werden würde. Zudem wird dadurch eigentlich kein dringendes Problem behoben.
Schlußwort
Der Typ Object
ist nicht als Basistyp von Schnittstellentypen anzusehen, wohl aber als Basistyp von Typen, die Schnittstellen implementieren. Dies steht nicht im Widerspruch dazu, daß es sich bei Schnittstellen um Typen handelt. Die Inkonsistenzen in der Bedeutung von Schnittstellen und die Unklarheit über ihre Basistypen sind entweder auf Mängel in der Planungsphase der CLR und des .NET Frameworks zurückzuführen oder es handelt sich um Spuren der geschichtlichen Gewordenheit der Technologien.
Ein allgemeines Bereinigen der Inkonsistenzen ist wohl aufgrund der Unmenge an Code, der bereits für .NET entwickelt wurde, nicht möglich. Die betrachteten Dokumentationsausschnitte und Implementierungen lassen den Schluß zu, daß es zumindest möglich ist, innerhalb der drei Bereiche CLR und .NET Framework, Visual Basic .NET und C# einen widerspruchsfreien Zustand herzustellen. Dokumentation und Objektbrowser können angepaßt werden, ohne bestehenden Code in Mitleidenschaft zu ziehen. Inkonsistenzen zwischen den Bereichen sind zwar ärgerlich, aber von geringerem Hindernis. Es bleibt zu hoffen, daß in zukünftigen Versionen des .NET Frameworks und der beiden Programmiersprachen versucht wird, bestehende Widersprüche auszuräumen.