|
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:
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 |