Warum hat das .NET Framework kein Konzept von Klassen als erstklassige Typen?


20

Kennern der Geschichte ist bekannt, dass C # und das .NET-Framework im Wesentlichen als "Delphi neu geschrieben, um sich wie Java zu fühlen", das vom Chefentwickler hinter Delphi, Anders Hejlsberg, entworfen wurde. Seitdem ist einiges anders gelaufen, aber die Ähnlichkeiten waren von Anfang an so offensichtlich, dass es sogar ernsthafte Spekulationen gab, dass .NET ursprünglich Borlands Produkt war.

Aber ich habe in letzter Zeit einige .NET-Sachen angeschaut, und eine der interessantesten und nützlichsten Funktionen von Delphi scheint gänzlich zu fehlen: das Konzept von Klassen als erstklassigem Datentyp. Für diejenigen, die nicht damit vertraut sind, stellt der Typ TClasseinen Verweis auf eine Klasse dar, ähnlich dem TypeTyp in .NET. Aber wo .NET Typefür die Reflexion verwendet, verwendet Delphi TClasseinen sehr wichtigen eingebauten Teil der Sprache. Es erlaubt verschiedene nützliche Redewendungen, die ohne sie einfach nicht existieren und nicht existieren können, wie Klassenuntertypvariablen und virtuelle Klassenmethoden.

Jede OO-Sprache verfügt über virtuelle Methoden, in denen verschiedene Klassen dasselbe grundlegende Konzept einer Methode auf unterschiedliche Weise implementieren, und dann wird zur Laufzeit die richtige Methode basierend auf dem tatsächlichen Typ der Objektinstanz aufgerufen, auf die sie aufgerufen wird. Delphi erweitert dieses Konzept auf Klassen: Wenn Sie eine TClass-Referenz als einen bestimmten Klassensubtyp definiert haben (dh class of TMyClass, die Variable kann jede Klassenreferenz akzeptieren, die von einer TMyClassvirtuellen Methode im Klassenbereich erbt , jedoch nichts außerhalb der Hierarchie) In diesem Fall können sie ohne Instanz aufgerufen werden, indem der tatsächliche Typ der Klasse verwendet wird. Das Anwenden dieses Musters auf Konstruktoren macht beispielsweise eine Factory-Implementierung trivial.

In .NET scheint es nichts Äquivalentes zu geben. Hat jemand mit so nützlichen Klassenreferenzen (und insbesondere virtuellen Konstruktoren und anderen Methoden für virtuelle Klassen!) Etwas darüber gesagt, warum sie ausgelassen wurden?

Spezifische Beispiele

Formular-Deserialisierung

Die Delphi-VCL speichert Formulare im DFMFormat einer DSL zur Beschreibung einer Komponentenhierarchie. Wenn der Formularleser DFM-Daten analysiert, werden Objekte durchsucht, die wie folgt beschrieben werden:

object Name: ClassName
   property = value
   property = value
   ...
   object SubObjectName: ClassName
      ...
   end
end

Das Interessante hier ist der ClassNameTeil. Jede Komponentenklasse registriert sich zu einem bestimmten Zeitpunkt TClassbeim Komponenten-Streaming-System initialization(man denke, statische Konstruktoren, die sich nur geringfügig voneinander unterscheiden und die garantiert sofort beim Start auftreten). Dadurch wird jede Klasse in einer Zeichenfolge-> TClass-Hashmap mit dem Klassennamen als Schlüssel registriert.

Jede Komponente stammt von ab TComponent, die über einen virtuellen Konstruktor verfügt, der ein einzelnes Argument akzeptiert Owner: TComponent. Jede Komponente kann diesen Konstruktor überschreiben, um eine eigene Initialisierung zu ermöglichen. Wenn der DFM-Leser einen Klassennamen liest, schlägt er den Namen in der oben genannten Hashmap nach und ruft die entsprechende Klassenreferenz ab (oder löst eine Ausnahme aus, wenn sie nicht vorhanden ist). Anschließend ruft er den virtuellen TComponent-Konstruktor auf, der als gut bekannt ist weil die Registrierungsfunktion eine Klassenreferenz verwendet, die erforderlich ist, um von TComponent abzuleiten, und Sie am Ende ein Objekt des richtigen Typs haben.

Fehlt dies, ist das WinForms-Äquivalent ... na ja ... ein großes Durcheinander, um es klar auszudrücken, und es ist erforderlich, dass eine neue .NET-Sprache ihre eigene Formular- (De-) Serialisierung vollständig neu implementiert. Das ist ein bisschen schockierend, wenn man darüber nachdenkt. Da es bei einer CLR darauf ankommt, dass mehrere Sprachen dieselbe Basisinfrastruktur verwenden, wäre ein DFM-ähnliches System durchaus sinnvoll gewesen.

Erweiterbarkeit

Eine von mir geschriebene Image-Manager-Klasse kann mit einer Datenquelle versehen werden (z. B. einem Pfad zu Ihren Image-Dateien) und dann automatisch neue Image-Objekte laden, wenn Sie versuchen, einen Namen abzurufen, der nicht in der Sammlung enthalten, aber in der Datenquelle verfügbar ist. Es verfügt über eine Klassenvariable, die als class ofBasis-Image-Klasse angegeben wird und die Klasse aller neu zu erstellenden Objekte darstellt. Standardmäßig sind die Bilder so eingestellt, dass sie bei der Erstellung neuer Bilder für bestimmte Zwecke auf unterschiedliche Weise eingerichtet werden müssen. (Erstellen ohne Alphakanal, Abrufen spezieller Metadaten aus einer PNG-Datei zur Angabe der Sprite-Größe usw.)

Dies könnte erreicht werden, indem umfangreiche Mengen an Konfigurationscode geschrieben und spezielle Optionen an alle Methoden übergeben werden, mit denen möglicherweise ein neues Objekt erstellt wird ... oder indem einfach eine Unterklasse der Basisimageklasse erstellt wird, die eine virtuelle Methode überschreibt, bei der Folgendes zutrifft Der betreffende Aspekt wird konfiguriert. Verwenden Sie anschließend einen try / finally-Block, um die Eigenschaft "default class" nach Bedarf vorübergehend zu ersetzen und anschließend wiederherzustellen. Dies mit Klassenreferenzvariablen zu tun ist viel einfacher und kann nicht stattdessen mit Generika erfolgen.



3
Können Sie ein oder zwei konkrete Beispiele nennen, bei denen a TClassmit einigem Beispielcode nützlich ist? In meiner flüchtigen Internetrecherche über finde TClassich, dass TClassdas als Parameter herumgereicht werden kann. Dies geschieht in .NET mit Generics. Factory-Methoden werden einfach staticin .NET markiert und erfordern keine Klasseninstanz zur Ausführung.
Robert Harvey

7
@RobertHarvey: Für mich persönlich, C # begann viel mehr Sinn zu machen , wenn ich aufgehört es als Java / C ++ Suche neu geladen und gestartet als Modula-2 mit Objekten zu behandeln. Bei Java ist es genauso: Alle sagen, es wurde von C ++ beeinflusst, aber das stimmt nicht. Sein Haupteinfluss ist Objective-C (und Smalltalk darüber), und Java wird viel sinnvoller, wenn Sie es als Smalltalk mit Typen anstelle von C ++ mit GC behandeln.
Jörg W Mittag

3
@RobertHarvey: TClassist eine grundlegende Sprachfunktion , die Compiler-Unterstützung erfordert. Sie können nicht "selbst schreiben", ohne Ihre eigene Sprache zu schreiben, und für .NET würde dies nicht ausreichen, da das Objektmodell von der CLR und nicht von einzelnen Sprachen definiert wird. Es ist etwas, das buchstäblich Teil des .NET Frameworks selbst sein muss, oder es kann nicht existieren.
Mason Wheeler

4
Zunächst kann ich mit größter Sicherheit sagen, dass .NET ursprünglich kein "Borland" -Produkt war. Woher weiß ich das? Ich war (bin) immer noch Teil des ursprünglichen Kernteams, das Delphi entwickelt hat. Ich habe eng mit Anders, Chuck, Gary und anderen zusammengearbeitet. Natürlich bin ich mir sicher, dass Sie das wissen. Was das Vorhandensein einer Klassenreferenz (wie TClass und ähnliche Konstrukte genannt werden) in .NET angeht, wurde dies wahrscheinlich als unnötig erachtet, da zur Laufzeit umfangreiche zugreifbare Typinformationen vorliegen. Delphi hatte am Anfang weit weniger Typinformationen und Klassenreferenzen waren ein Kompromiss.
Allen Bauer

Antworten:


5

.NET (die CLR) war die dritte Generation des Component Object Model (COM) von Microsoft, das in den Anfängen als "COM + -Runtime" bezeichnet wurde. Microsoft Visual Basic und der Markt für COM / ActiveX-Steuerelemente haben einen wesentlich stärkeren Einfluss auf die Auswahl der CLR-Architektur-Kompatibilität als Borland Delphi. (Zugegebenermaßen hat die Tatsache, dass Delphi ActiveX-Steuerelemente übernommen hat, sicherlich dazu beigetragen, das ActiveX-Ökosystem zu vergrößern, aber COM / ActiveX existierte vor Delphi.)

Die Architektur von COM wurde in C (nicht in C ++) ausgearbeitet und konzentrierte sich eher auf Schnittstellen als auf Klassen. Darüber hinaus wird die Objektzusammensetzung unterstützt, dh ein COM-Objekt kann tatsächlich aus mehreren verschiedenen Objekten bestehen, die über die IUnknown-Schnittstelle miteinander verbunden werden. IUnknown hatte jedoch keine Rolle bei der Erstellung von COM-Objekten, die so sprachunabhängig wie möglich gestaltet wurden. Die Objekterstellung wurde im Allgemeinen von IClassFactory und die Reflektion von ITypeLibrary und verwandten Schnittstellen durchgeführt. Diese Trennung der Bedenken war unabhängig von der Implementierungssprache, und die Merkmale jeder COM-Kernschnittstelle wurden minimal und orthogonal gehalten.

Aufgrund der Popularität von COM- und ActiveX-Steuerelementen wurde die .NET-Architektur für die Unterstützung von COM IUnknown, IClassFactory und ITypeLibrary entwickelt. In COM befanden sich diese Schnittstellen nicht unbedingt auf demselben Objekt, sodass es nicht unbedingt Sinn machte, sie zusammenzufassen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.