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 TClass
einen Verweis auf eine Klasse dar, ähnlich dem Type
Typ in .NET. Aber wo .NET Type
für die Reflexion verwendet, verwendet Delphi TClass
einen 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 TMyClass
virtuellen 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 DFM
Format 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 ClassName
Teil. Jede Komponentenklasse registriert sich zu einem bestimmten Zeitpunkt TClass
beim 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 of
Basis-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.
TClass
mit einigem Beispielcode nützlich ist? In meiner flüchtigen Internetrecherche über finde TClass
ich, dass TClass
das als Parameter herumgereicht werden kann. Dies geschieht in .NET mit Generics. Factory-Methoden werden einfach static
in .NET markiert und erfordern keine Klasseninstanz zur Ausführung.
TClass
ist 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.