class-reference type

Top  Previous  Next

What is translated > class-reference type

Delphi supports class-reference types. A class-reference type is declared with the syntax `class of ...` and can hold a reference to a class, not to an object instance.

 

Example:

 

type

  TBase = class

  end;

 

  TBaseClass = class of TBase;

 

  TDerived = class(TBase)

  end;

 

function Make(Base: TBaseClass): TBase;

begin

  Result := Base.Create;

end;

 

 

In this example, `Base` does not contain an object of type `TBase`. It contains a reference to a class derived from `TBase`. Depending on the value passed to `Make`, the expression

 

 

Base.Create

 

 

can create either a `TBase` instance or an instance of a descendant class such as `TDerived`.

 

C# has no direct equivalent to Delphi class-reference types. Static methods in C# can be called without creating an object instance, but a class itself cannot normally be assigned to a variable and then used like a Delphi metaclass.

 

To support Delphi class references, Delphi2C# now uses a runtime metaclass type named `TClass`.

 

TClass

 

`TClass` is the C# runtime representation of a Delphi class reference. Internally, it stores the corresponding .NET `System.Type` and provides Delphi-like operations such as:

 

 

ClassName()

ClassParent()

ClassType()

InheritsFrom(...)

Create()

 

 

A Delphi class-reference declaration such as

 

type

  TBaseClass = class of TBase;

 

is represented in generated C# code as a `TClass` value.

 

A Delphi class expression such as

 

TDerived

 

is translated to a `TClass` value, for example:

 

TClass.Of<TDerived>()

 

This avoids the older approach where Delphi2C# generated a parallel hierarchy of `ClassRef<T>` classes and inserted additional helper methods such as `ClassName`, `ClassType`, `ClassParent`, `Create`, and `SCreate` into every translated class.

 

The current approach is simpler and keeps the generated classes closer to their original structure.

 

Example

 

Delphi code:

 

type

  TBase = class

  public

    function GetName: String; virtual;

  end;

 

  TBaseClass = class of TBase;

 

  TDerived = class(TBase)

  public

    function GetName: String; override;

  end;

 

function Make(Base: TBaseClass): TBase;

begin

  Result := Base.Create;

end;

 

function TestFactory: Boolean;

var

  P: TBase;

begin

  P := Make(TDerived);

  Result := P.GetName = 'TDerived';

end;

 

 

Possible generated C# code:

 

using static System.SystemInterface;

 

namespace Test

{

    public static class Test

    {

        public class TBase : TObject

        {

            public virtual string GetName()

            {

                return "TBase";

            }

        }

 

        public class TDerived : TBase

        {

            public override string GetName()

            {

                return "TDerived";

            }

        }

 

        public static TBase Make(TClass Base)

        {

            TBase result;

 

            result = (TBase)Base.Create();

 

            return result;

        }

 

        public static bool TestFactory()

        {

            TBase P;

            bool result;

 

            P = Make(TClass.Of<TDerived>());

            result = P.GetName() == "TDerived";

 

            return result;

        }

    }

}

 

 

The central point is the conversion of the Delphi class expression:

 

TDerived

 

to the C# runtime metaclass value:

 

TClass.Of<TDerived>()

 

The factory function then calls:

 

Base.Create()

 

on the `TClass` value.

 

ClassType, ClassName, ClassParent and InheritsFrom

 

Delphi also allows class-reference operations through object instances:

 

ClassRef := Sender.ClassType;

 

while ClassRef <> nil do

begin

  S := ClassRef.ClassName;

  ClassRef := ClassRef.ClassParent;

end;

 

In generated C# code this is represented using `TClass`:

 

TClass ClassRef;

 

ClassRef = Sender.ClassType();

 

while (ClassRef != null)

{

    S = ClassRef.ClassName();

    ClassRef = ClassRef.ClassParent();

}

 

 

The following Delphi expressions are translated to `TClass`-based operations:

 

 

Obj.ClassType

Obj.ClassName

Obj.InheritsFrom(TBase)

SomeClassRef.ClassName

SomeClassRef.ClassParent

SomeClassRef.InheritsFrom(TBase)

 

 

Typical generated C# equivalents are:

 

 

Obj.ClassType()

Obj.ClassName()

Obj.InheritsFrom(TClass.Of<TBase>())

SomeClassRef.ClassName()

SomeClassRef.ClassParent()

SomeClassRef.InheritsFrom(TClass.Of<TBase>())

 

 

This makes metaclass comparisons and inheritance tests possible without generating additional class-reference helper classes.

 

Example:

 

Result := Obj.ClassType = TDerived;

Result := Obj.InheritsFrom(TBase);

 

 

Generated C#:

 

 

result = Obj.ClassType() == TClass.Of<TDerived>();

result = Obj.InheritsFrom(TClass.Of<TBase>());

 

 

Object creation through class references

 

`TClass.Create()` creates an instance of the class represented by the `TClass` value.

 

For example:

 

function Make(Base: TBaseClass): TBase;

begin

  Result := Base.Create;

end;

 

can be translated to:

 

public static TBase Make(TClass Base)

{

    return (TBase)Base.Create();

}

 

 

This requires that the represented class can be created with a suitable constructor. For a parameterless Delphi constructor, the generated C# class must provide a corresponding parameterless C# constructor.

 

If the Delphi code calls a constructor with parameters through a class reference, Delphi2C# must generate a matching `TClass.Create(...)` call or use reflection-based construction.

 

Example Delphi code:

 

type

  TCreateWithStringParam = class

  public

    constructor Create(S: String);

  end;

 

function Make2(ClassRefParam: TCreateWithStringParamClass; S: String): TCreateWithStringParam;

begin

  Result := ClassRefParam.Create(S);

end;

 

 

A possible C# representation is:

 

public static TCreateWithStringParam Make2(TClass ClassRefParam, string S)

{

    return (TCreateWithStringParam)ClassRefParam.Create(S);

}

 

 

Virtual class methods

 

Delphi supports virtual class methods:

 

type

  TBase = class

  public

    class function ClassVirtual(X: Integer): Integer; virtual;

  end;

 

  TDerived = class(TBase)

  public

    class function ClassVirtual(X: Integer): Integer; override;

  end;

 

 

C# does not support virtual static methods in the same way as Delphi class methods. Therefore Delphi2C# represents virtual class-method dispatch through `TClass`.

 

A Delphi call such as

 

 

TDerived.ClassVirtual(1)

 

 

or a virtual class-method call through an instance can be translated to a metaclass dispatch operation, for example:

 

TClass.Of<TDerived>().InvokeClassInt("ClassVirtual", 1);

 

 

or:

 

Obj.ClassType().InvokeClassInt("ClassVirtual", 1);

 

 

The exact generated helper method depends on the method signature. The important point is that virtual class-method dispatch is based on the represented runtime class, not on C# static method dispatch.

 

Non-virtual Delphi class methods, on the other hand, are translated as normal C# static methods, because their behavior is closer to C# static method calls.

 

Exceptions

 

Earlier versions of Delphi2C# used a separate `ExceptionRef<T>` hierarchy because Delphi exceptions derive from `TObject`, while C# exceptions must derive from `System.Exception`.

 

With the current `TClass`-based model, class-reference handling is centralized in the runtime metaclass representation. Exception creation can also be handled by `TClass` or by specialized runtime helpers where required.

 

The reason for special handling remains the same: a C# object can only be thrown and caught as an exception if it derives from `System.Exception`. Therefore translated Delphi exception classes must be represented by C# exception classes, and exception factory code may need to use constructor signatures such as:

 

 

Create(string message)

 

 

instead of the normal `TObject` creation path.

 

For example, Delphi code that creates an exception from a class reference:

 

E := ExceptionClass.Create(Message);

 

 

can be represented using runtime metaclass construction:

 

E = (Exception)ExceptionClass.Create(Message);

 

 

where `ExceptionClass` is a `TClass` value representing a translated exception class.

 

Notes and limitations

 

`TClass` provides a practical framework for common Delphi class-reference operations, including:

 

assigning class references to variables
comparing class references
retrieving class names
walking the class-parent chain
testing inheritance with `InheritsFrom`
creating objects through class references
dispatching virtual class methods through metaclass information

 

This is still a compatibility layer. C# does not have a native feature that is exactly equivalent to Delphi `class of` types. Therefore Delphi2C# translates class-reference operations to runtime helper calls based on `TClass`.

 

The new implementation no longer requires a generated `ClassRef<T>` hierarchy and no longer needs special `ClassName`, `ClassType`, `ClassParent`, `Create`, and `SCreate` methods to be inserted into every translated class. This reduces generated boilerplate and makes the generated C# code easier to read and maintain.

 



This page belongs to the Delphi2C# Documentation

Delphi2C# home  Content