Pourquoi le framework .NET n'a-t-il aucun concept de classes en tant que types de première classe?


20

Il est bien connu de ceux qui connaissent l'histoire que C # et le framework .NET ont commencé comme étant essentiellement "Delphi réécrit pour ressembler à Java", architecturé par le développeur en chef de Delphi, Anders Hejlsberg. Les choses ont beaucoup divergé depuis lors, mais au début, les similitudes étaient si évidentes qu'il y avait même de sérieuses spéculations selon lesquelles .NET était en fait à l'origine le produit de Borland.

Mais j'ai récemment regardé quelques trucs .NET, et l'une des fonctionnalités les plus intéressantes et utiles de Delphi semble manquer complètement: le concept de classes en tant que type de données de première classe. Pour ceux qui ne le connaissent pas, le type TClassreprésente une référence à une classe, similaire au Typetype dans .NET. Mais là où .NET utilise Typepour la réflexion, Delphi utilise TClasscomme une partie intégrée très importante du langage. Il permet divers idiomes utiles qui n'existent tout simplement pas et ne peuvent pas exister sans lui, tels que les variables de sous-type de classe et les méthodes de classe virtuelle.

Chaque langage OO possède des méthodes virtuelles, dans lesquelles différentes classes implémentent le même concept fondamental d'une méthode de différentes manières, puis la bonne méthode est appelée au moment de l'exécution en fonction du type réel de l'instance d'objet sur laquelle elle est appelée. Delphi étend ce concept aux classes: si vous avez une référence TClass définie comme un sous-type de classe spécifique (c'est-à- class of TMyClassdire que la variable peut accepter n'importe quelle référence de classe qui hérite TMyClass, mais pas en dehors de la hiérarchie) qui a des méthodes virtuelles de portée de classe attachées à , ils peuvent être appelés sans instance en utilisant le type réel de la classe. L'application de ce modèle aux constructeurs rend une implémentation Factory triviale, par exemple.

Il ne semble pas y avoir d'équivalent dans .NET. Avec aussi utiles que les références de classe (et en particulier les constructeurs virtuels et autres méthodes de classe virtuelle!), Est-ce que quelqu'un a dit pourquoi ils ont été exclus?

Exemples spécifiques

Désérialisation de formulaire

Delphi VCL enregistre les formulaires au DFMformat, un DSL pour décrire une hiérarchie de composants. Lorsque le lecteur de formulaire analyse les données DFM, il s'exécute sur des objets décrits comme suit:

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

La chose intéressante ici est la ClassNamepartie. Chaque classe de composant enregistre son temps TClassavec le système de streaming de composants initialization(pensez aux constructeurs statiques, seulement légèrement différents, garantis de se produire immédiatement au démarrage.) Cela enregistre chaque classe dans une chaîne -> TClass hashmap avec le nom de classe comme clé.

Chaque composant descend TComponent, ce qui a un constructeur virtuel qui prend un seul argument, Owner: TComponent. Tout composant peut remplacer ce constructeur pour prévoir sa propre initialisation. Lorsque le lecteur DFM lit un nom de classe, il recherche le nom dans la table de hachage susmentionnée et récupère la référence de classe correspondante (ou déclenche une exception s'il n'est pas là), puis appelle le constructeur virtuel TComponent dessus, qui est connu pour être bon car la fonction d'enregistrement prend une référence de classe qui doit descendre de TComponent et vous vous retrouvez avec un objet du type approprié.

À défaut, l'équivalent de WinForms est ... eh bien ... un gros gâchis pour le dire franchement, nécessitant tout nouveau langage .NET pour réimplémenter complètement sa propre forme (de) sérialisation. C'est un peu choquant quand on y pense; comme l'intérêt d'avoir un CLR est de laisser plusieurs langues utiliser la même infrastructure de base, un système de style DFM aurait été parfaitement logique.

Extensibilité

Une classe de gestionnaire d'images que j'ai écrite peut être fournie avec une source de données (comme un chemin d'accès à vos fichiers image), puis charger automatiquement de nouveaux objets image si vous essayez de récupérer un nom qui n'est pas dans la collection mais qui est disponible dans la source de données. Il a une variable de classe typée comme class ofclasse d'image de base, représentant la classe de tout nouvel objet à créer. Il est livré avec une valeur par défaut, mais il y a certains points, lors de la création de nouvelles images à des fins spéciales, que les images doivent être configurées de différentes manières. (Création sans canal alpha, récupération de métadonnées spéciales à partir d'un fichier PNG pour spécifier la taille de l'image-objet, etc.)

Cela pourrait être fait en écrivant de grandes quantités de code de configuration et en passant des options spéciales à toutes les méthodes qui pourraient finir par créer un nouvel objet ... ou vous pourriez simplement créer une sous-classe de la classe d'image de base qui remplace une méthode virtuelle où l'aspect en question est configuré, puis utilisez un bloc try / finally pour remplacer temporairement la propriété "classe par défaut" selon vos besoins, puis restaurez-la. Le faire avec des variables de référence de classe est beaucoup plus simple et n'est pas quelque chose qui pourrait être fait avec des génériques à la place.



3
Pouvez-vous fournir un ou deux exemples concrets où a TClassest utile, avec un exemple de code? Dans ma recherche Internet rapide sur TClass, je trouve que cela TClasspeut être transmis comme paramètre. Cela se fait dans .NET en utilisant des génériques. Les méthodes d'usine sont simplement marquées staticen .NET et ne nécessitent pas d'instance de classe pour s'exécuter.
Robert Harvey

7
@RobertHarvey: Pour moi, personnellement, C # a commencé à avoir beaucoup plus de sens une fois que j'ai arrêté de le regarder lorsque Java / C ++ était rechargé et que je commençais à le traiter comme Modula-2 avec des objets. C'est la même chose avec Java: tout le monde dit qu'il a été influencé par C ++, mais ce n'est pas vrai. Son influence principale est Objective-C (et Smalltalk via cela), et Java aura beaucoup plus de sens une fois que vous le traiterez comme Smalltalk avec des types au lieu de C ++ avec GC.
Jörg W Mittag

3
@RobertHarvey: TClassest une fonctionnalité de langage fondamentale qui nécessite le support du compilateur. Vous ne pouvez pas "écrire le vôtre" sans écrire votre propre langage, et pour .NET même cela ne serait pas suffisant car le modèle d'objet est défini par le CLR, pas par des langages individuels. C'est quelque chose qui doit littéralement faire partie du framework .NET lui-même, ou il ne peut pas exister.
Mason Wheeler

4
Tout d'abord, je peux dire avec la plus grande certitude que .NET n'était pas un produit "Borland" à l'origine. Comment le sais-je? Je faisais (toujours) partie de l'équipe de base d'origine qui a développé Delphi. J'ai travaillé en étroite collaboration avec Anders, Chuck, Gary et d'autres. Bien sûr, je suis certain que vous le savez. Quant à l'existence d'une référence de classe (comme TClass et les constructions similaires sont appelées) dans .NET a probablement été jugé inutile en raison de l'existence de riches informations de type accessibles au moment de l'exécution. Delphi avait beaucoup moins d'informations de type au début et les références de classe étaient un compromis.
Allen Bauer

Réponses:


5

.NET (le CLR) était la troisième génération du modèle d'objet composant (COM) de Microsoft, qui, au début, était appelé «runtime COM +». Microsoft Visual Basic et le marché des contrôles COM / ActiveX ont eu beaucoup plus d'influence sur les choix de compatibilité architecturale CLR spécifiques que Borland Delphi. (Certes, le fait que Delphi ait adopté des contrôles ActiveX a certainement contribué à développer l'écosystème ActiveX, mais COM / ActiveX existait avant Delphi)

L'architecture de COM a été élaborée en C (pas C ++) et s'est concentrée sur les interfaces, plutôt que sur les classes. En outre, la composition d'objets était prise en charge, ce qui signifie qu'un objet COM pouvait en fait être composé de plusieurs objets différents, l'interface IUnknown les liant entre eux. Mais IUnknown n'a joué aucun rôle dans la création d'objets COM, conçue pour être aussi indépendante du langage que possible. La création d'objets était généralement gérée par IClassFactory, et la réflexion par ITypeLibrary et les interfaces associées. Cette séparation des préoccupations était indépendante du langage d'implémentation, et les fonctionnalités de chaque interface COM de base étaient maintenues minimales et orthogonales.

Ainsi, en raison de la popularité des contrôles COM et ActiveX, l'architecture .NET a été conçue pour prendre en charge COM IUnknown, IClassFactory et ITypeLibrary. Dans COM, ces interfaces n'étaient pas nécessairement sur le même objet, donc les regrouper n'avait pas nécessairement de sens.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.