A feature overlooked by many Delphi programmer was already introduced in Delphi 1 which is more or less the same as in the Delphi 2007 documentation at [WayBack] Type Compatibility and Identity.
There is a distinction between these explained in the above link:
type TMyInteger1 = Integer; TMyInteger2 = type Integer;
Where TMyInteger1
is an alias for Integer
, TMyInteger2
introduces a new type which is distinct from Integer
and TMyInteger
. That way the compiler can set them apart, and even generates separate RTTI (Run-Time TypeInformation) for them.
Probably the most used distinct types are these:
TDateTime = type Double;
...
TDate = type TDateTime;
TTime = type TDateTime;
TFontName = type string
These are unlike TColor
which is defined as “just” a subrange of Integer, but because it is a subtype, also gets a distinct type:
TColor = -$7FFFFFFF-1..$7FFFFFFF;
Type identity is important because Delphi 1 introduced these mechanisms:
- the streaming instances and their properties
- editing instances and properties in the object inspector
- two way binding of designer (form/datamodule/frame/…) and the underlying Pascal source
Without them, very basic Delphi features would not work.
In addition, a lot of other RTTI based code now enables features like object relational mapping, binding to JSON/XML and many others.
What I did not know is that the Pascal and Delphi type systems have been heavily influenced by ADA. Luckily Lutz Donnerhacke pointed me to ADA [WayBack] Types and Subtypes.
Example
I made an example Distinct type types in Delphi · GitHub showing the differences on RTTI level in these properties:
property IntegerProperty: Integer read FIntegerField write FIntegerField;
property ColorProperty: TColor read FColorField write FColorField;
property DoubleProperty: Double read FDoubleField write FDoubleField;
property DateTimeProperty: TDateTime read FDateTimeField write FDateTimeField;
property DateProperty: TDate read FDateField write FDateField;
property TimeProperty: TTime read FTimeField write FTimeField;
property StringProperty: string read FStringField write FStringField;
property FontNameProperty: TFontName read FFontNameField write FFontNameField;
The generated table (see also the source below using [Archive.is] TRttiContext
added in Delphi 2010) indeed shows distinct types on the RTTI level:
Name
Type.Name
Type.QualifiedName
Type.TypeKind
IntegerProperty
Integer
System.Integer
tkInteger
ColorProperty
TColor
System.UITypes.TColor
tkInteger
DoubleProperty
Double
System.Double
tkFloat
DateTimeProperty
TDateTime
System.TDateTime
tkFloat
DateProperty
TDate
System.TDate
tkFloat
TimeProperty
TTime
System.TTime
tkFloat
StringProperty
string
System.string
tkUString
FontNameProperty
TFontName
System.UITypes.TFontName
tkUString
This post was inspired by an interesting discussion on [WayBack] What’s the technical term for the following construct: type intx = type integer; type inty = integer; What term would you use to describe the differen… – Johan Bontes – Google+
Documentation:
- More Delphi PDF manuals (via: Cape Cod Gunny Does Delphi: The One Manual Every Delphi Programmer Should Have!)
- [WayBack] Type Compatibility and Identity.
- [WayBack] Type Compatibility and Identity (Delphi) – RAD Studio
- [WayBack] Types and Subtypes
RTTI dump inspired by [WayBack] delphi – How can I distinguish TDateTime properties from Double properties with RTTI? – Stack Overflow.
–jeroen
program RttiForDistinctTypedTypesConsoleProject; | |
{$APPTYPE CONSOLE} | |
{$R *.res} | |
uses | |
// System, | |
System.Classes, | |
System.SysUtils, | |
System.Rtti, | |
System.UITypes; | |
// Dump based on https://stackoverflow.com/questions/7836880/how-can-i-distinguish-tdatetime-properties-from-double-properties-with-rtti | |
// The easiest to dump RTTI is by using a construct that: | |
// 1. gives you a TypeInfo type information handle, like a class having a ClassInfo function. | |
// 2. allows easy enumeration (again a class) | |
type | |
TInstance = class | |
strict private | |
FIntegerField: Integer; | |
FColorField: TColor; | |
FDoubleField: Double; | |
FDateTimeField: TDateTime; | |
FDateField: TDate; | |
FTimeField: TTime; | |
FStringField: string; | |
FFontNameField: TFontName; | |
public | |
property IntegerProperty: Integer read FIntegerField write FIntegerField; | |
property ColorProperty: TColor read FColorField write FColorField; | |
property DoubleProperty: Double read FDoubleField write FDoubleField; | |
property DateTimeProperty: TDateTime read FDateTimeField write FDateTimeField; | |
property DateProperty: TDate read FDateField write FDateField; | |
property TimeProperty: TTime read FTimeField write FTimeField; | |
property StringProperty: string read FStringField write FStringField; | |
property FontNameProperty: TFontName read FFontNameField write FFontNameField; | |
end; | |
type | |
TMarkdownSeparator = (Line, Row); | |
procedure MarkdownDump( | |
const aMarkdownSeparator: TMarkdownSeparator; | |
const aName: string; | |
const aType: string; | |
const aQualifiedType: string; | |
const aKind: string); | |
var | |
FixString: string; | |
SeparatorString: string; | |
begin | |
case aMarkdownSeparator of | |
Line: | |
begin | |
FixString := '–'; | |
SeparatorString := '-|-'; | |
end; | |
Row: | |
begin | |
FixString := '`'; | |
SeparatorString := '` | `'; | |
end; | |
end; | |
Writeln(Format('%s%s%s%s%s%s%s%s%s', [ | |
FixString, | |
aName, SeparatorString, | |
aType, SeparatorString, | |
aQualifiedType, SeparatorString, | |
aKind, | |
FixString | |
])); | |
end; | |
procedure Main; | |
var | |
RttiContext: TRttiContext; | |
InstanceRttiType: TRttiType; | |
RttiProperty : TRttiProperty; | |
PropertyType: TRttiType; | |
begin | |
RttiContext := TRttiContext.Create(); | |
MarkdownDump( | |
Row, | |
'Name', | |
'Type.Name', | |
'Type.QualifiedName', | |
'Type.TypeKind' | |
); | |
MarkdownDump(Line, '–', '–', '–', '–'); | |
try | |
InstanceRttiType := RttiContext.GetType(TInstance.ClassInfo); | |
for RttiProperty in InstanceRttiType.GetProperties() do | |
begin | |
PropertyType := RttiProperty.PropertyType; | |
MarkdownDump( | |
Row, | |
RttiProperty.Name, | |
PropertyType.Name, | |
PropertyType.QualifiedName, | |
TRttiEnumerationType.GetName<TTypeKind>(PropertyType.TypeKind) | |
); | |
end; | |
finally | |
RttiContext.Free(); | |
end; | |
end; | |
begin | |
try | |
Main(); | |
except | |
on E: Exception do | |
Writeln(E.ClassName, ': ', E.Message); | |
end; | |
end. |
Name |
Type.Name |
Type.QualifiedName |
Type.TypeKind |
---|---|---|---|
IntegerProperty |
Integer |
System.Integer |
tkInteger |
ColorProperty |
TColor |
System.UITypes.TColor |
tkInteger |
DoubleProperty |
Double |
System.Double |
tkFloat |
DateTimeProperty |
TDateTime |
System.TDateTime |
tkFloat |
DateProperty |
TDate |
System.TDate |
tkFloat |
TimeProperty |
TTime |
System.TTime |
tkFloat |
StringProperty |
string |
System.string |
tkUString |
FontNameProperty |
TFontName |
System.UITypes.TFontName |
tkUString |