20

It's well known to those familiar with the history that C# and the .NET framework started out as essentially "Delphi rewritten to feel like Java," architected by the chief developer behind Delphi, Anders Hejlsberg. Things have diverged quite a bit since then, but early on the similarities were so obvious that there was even some serious speculation that .NET was actually originally Borland's product.

But I've been looking at some .NET stuff lately, and one of the most interesting and useful features from Delphi seems to be missing entirely: the concept of classes as a first-class data type. For those not familiar with it, the type TClass represents a reference to a class, similar to the Type type in .NET. But where .NET uses Type for reflection, Delphi uses TClass as a very important built-in part of the language. It allows for various useful idioms that simply don't and can't exist without it, such as class subtype variables and virtual class methods.

Every OO language has virtual methods, in which different classes implement the same fundamental concept of a method in different ways, and then the right method gets called at runtime based on the actual type of the object instance it's called on. Delphi extends this concept to classes: if you have a TClass reference defined as a specific class subtype (ie class of TMyClass means that the variable can accept any class reference that inherits from TMyClass, but not anything outside the heirarchy) that has class-scope virtual methods attached to it, they can be called without an instance by using the actual type of the class. Applying this pattern to constructors makes a Factory implementation trivial, for example.

There doesn't seem to be anything equivalent in .NET. With as useful as class references (and especially virtual constructors and other virtual class methods!) are, has anyone said anything about why they were left out?

Specific Examples

Form Deserialization

The Delphi VCL saves forms in DFM format, a DSL for describing a component hierarchy. When the form reader parses DFM data, it runs across objects that are described like this:

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

The interesting thing here is the ClassName part. Each component class registers its TClass with the component streaming system at initialization time (think static constructors, only slightly different, guaranteed to happen immediately on startup.) This registers each class in a string->TClass hashmap with the class name as the key.

Each component descends from TComponent, which has a virtual constructor that takes a single argument, Owner: TComponent. Any component can override this constructor to provide for its own initialization. When the DFM reader reads a class name, it looks up the name in the aforementioned hashmap and retrieves the corresponding class reference (or raises an exception if it's not there), then calls the virtual TComponent constructor on it, which is known to be good because the registration function takes a class reference that is required to descend from TComponent, and you end up with an object of the proper type.

Lacking this, the WinForms equivalent is... well... a big mess to put it bluntly, requiring any new .NET language to completely re-implement its own form (de)serialization. This is a bit shocking when you think about it; since the whole point of having a CLR is to let multiple languages use the same basic infrastructure, a DFM-style system would have made perfect sense.

Extensibility

An image manager class I wrote can be provided with a data source (such as a path to your image files) and then load new image objects automatically if you attempt to retrieve a name that's not in the collection but is available in the data source. It has a class variable typed as class of the base image class, representing the class of any new objects to be created. It comes with a default, but there are some points, when creating new images with special purposes, that the images should be set up in different ways. (Creating it without an alpha channel, retrieving special metadata from a PNG file to specify sprite size, etc.)

This could be done by writing extensive amounts of configuration code and passing in special options to all of the methods that might end up creating a new object... or you could just make a subclass of the base image class that overrides a virtual method where the aspect in question gets configured, and then use a try/finally block to temporarily replace the "default class" property as needed and then restore it. Doing it with class reference variables is far simpler, and is not something that could be done with generics instead.

28
  • 3
  • 3
    Can you provide one or two concrete examples where a TClass is useful, with some sample code? In my cursory internet research about TClass, I am finding that TClass can be passed around as a parameter. This is done in .NET using Generics. Factory methods are simply marked static in .NET, and don't require a class instance to execute. Commented Aug 25, 2014 at 18:22
  • 8
    @RobertHarvey: To me, personally, C# started to make a lot more sense once I stopped looking at it as Java/C++ reloaded and started treating it as Modula-2 with objects. It's the same with Java: everybody says it was influenced by C++, but that's not true. Its main influence is Objective-C (and Smalltalk via that), and Java will make a lot more sense once you treat it as Smalltalk with types instead of C++ with GC. Commented Aug 25, 2014 at 19:18
  • 3
    @RobertHarvey: TClass is a fundamental language feature that requires compiler support. You can't "write your own" without writing your own language, and for .NET even that would not be sufficient because the object model is defined by the CLR, not by individual languages. It's something that literally needs to be part of the .NET framework itself, or it can't exist. Commented Aug 25, 2014 at 20:45
  • 5
    First of all, I can say with utmost certainty that .NET wasn't a "Borland" product originally. How do I know this? I was (still am) part of the original core team which developed Delphi. I worked closely with Anders, Chuck, Gary and others. Of course, I'm certain that you know this. As for the existence of a class reference (as TClass and similar constructs are called) in .NET was likely deemed unnecessary due to the existence of rich run-time accessible type information. Delphi had far less type information in the beginning and class refs were a compromise. Commented Jan 19, 2015 at 5:51

2 Answers 2

5

.NET (the CLR) was the third generation of Microsoft's Component Object Model (COM), which in the early days referred to as the "COM+ runtime". Microsoft Visual Basic and the COM/ActiveX controls market has had much more influence on the specific CLR architectural compatability choices than Borland Delphi. (Admittedly the fact that that Delphi had adopted ActiveX controls certainly helped grow the ActiveX ecosystem, but COM/ActiveX existed before Delphi)

COM's architecture was worked out in C (not C++) and focused on interfaces, rather than classes. Furthermore supported object composition, meaning that a COM object could in fact be made up of several different objects, with the IUnknown interface linking them together. But IUnknown had no role in COM object creation, which was designed to be as language-independent as possible. Object creation was generally handled by IClassFactory, and reflection by ITypeLibrary and related interfaces. This separation of concerns was independent of implementation language, and the features of each core COM interface were kept minimal and orthogonal.

So as a result of the popularity of COM and ActiveX controls, .NET architecture was built to support on the COM IUnknown, IClassFactory, and ITypeLibrary. In COM, these interfaces were not necessarily on the same object, so lumping these together didn't necessarily make sense.

0

C# supports generics which have functionality overlapping with classes as first-class values. For example:

void Foo<TClass>() where TClass: TMyClass { ... }

will accept a type parameter which is a subtype of TMyClass. Generics are different from first-class types in that type parameters use a separate syntax from reqular parameters, but conceptually it is the same.

Given the support for generics there wouldn't be much benefit to simultaneously support classes as first-class values. And generics are more powerful in general, so it is not surprising c#/.net went in that direction.

Some features are not supported though: Overriding class-level members. This could also be supported through generics but is not. Most likely this feature was just not considered useful enough to include.

1
  • It was likely regarded too complex: Add it, and you take a big step to full templates. Commented Apr 1, 2023 at 10:40

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.