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:
NameType.NameType.QualifiedNameType.TypeKindIntegerPropertyIntegerSystem.IntegertkIntegerColorPropertyTColorSystem.UITypes.TColortkIntegerDoublePropertyDoubleSystem.DoubletkFloatDateTimePropertyTDateTimeSystem.TDateTimetkFloatDatePropertyTDateSystem.TDatetkFloatTimePropertyTTimeSystem.TTimetkFloatStringPropertystringSystem.stringtkUStringFontNamePropertyTFontNameSystem.UITypes.TFontNametkUString
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 |