Few people know about a Delphi language feature that has been present since Delphi 1: prepending the type definition with a type keyword to make the type getting a new identity.
Each time I use it, I have to do some browsing for the consequences, and this time I wrote down some notes and created a small example program (source is also below).
This time I needed it when writing class wrappers on top of the Delphi bindings for WebSphere MQ.
WebSphere MQ has Queues where you can put and get messages. It also has Queue Managers to which you connect, and that provide queuing services and manages queues.
Both Queues and Queue Managers have names that can be up to 48 (single byte) characters long.
Those names mean totally different things, so though the have similar data types, they have a different identity.
The same holds for 20 byte character arrays (they can be used as names for ChannelName, ShortConnectionName and MCAName). The 264 byte character array is so far used for ConnectionName only.
Distinguishing those types: That’s what “type types” in Delphi are all about.
The DocWiki tries to explain this in the Type Compatibility and Identity (Delphi) topic:
- Type Identity: if you add the keyword “type” in a declaration, that type gets a new identity
- Type Compatibility: there are rules about type compatibility
- Assignment Compatibility: variables of incompatible types can be assigned from an expression if the type of that expression is compatible when the type of the variable.
Type Identity has to do with RTTI: in the example below, MQCHAR48, TMQQueueManagerName and TMQQueueName have different identities, so there is different RTTI generated for them.
Assignment compatibility has to do both with the RTTI, and how relaxed some fundamental types are handled by the compiler.
The documentation on assignment incompatibility is incomplete:
- There is a very important difference between string constants and string variables:
- You can assign the various character array type values to a string variable, but not vice versa
- You can assign an implicit string constant to any of the character array type variables
- The character array type variables are not assignment compatible with each other
The latter was very important to me for two reasons:
- now I can make sure you can not assign TMQQueue.Name to TMQQueueManager.Name (even if instances of these two have similar names),
- and the different RTTI guarantees I can hook different property editors to each property.
A few notes:
- This is not limited to my examples that uses character arrays (as that’s what I needed in my particular case).
You can do the same with other types like integers, characters. pointers, etc. - For integer type types the assignment compatibility is less restrict than for character arrays (example “TImageIndex = type Integer;”)
- There is a very special usage when doing “type AnsiString(CodePage)“. Without the “type” the result would not have identity and would be assignment compatible. With CodePages, you want to limit assignment compatibility.
These types declared in the RTL each have their own identity:
- UNICODE_FSSString = type AnsiString(65001);
- SHIFT_JIS = type AnsiString(932);
- TOem437String = type AnsiString(437);
- UTF8String = type AnsiString(65001);
- RawByteString = type AnsiString($ffff);
–jeroen
via:
- Type Compatibility and Identity (Delphi) – RAD Studio XE2.
- Joe White’s Blog » Blog Archive » Grammar details of Delphi’s “type type” feature.
- On the type compatibility in Delphi « The Programming Works.
- Question about type identity – delphi.
program TypeIdentity; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils; type Range20 = 0..19; Range48 = 0..47; Range264 = 0..263; SingleByteChar = AnsiChar; // Single-Byte Character; WebSphereMQ does not support Unicode MQCHAR = SingleByteChar; MQCHAR20 = array[Range20] of MQCHAR; MQCHAR48 = array[Range48] of MQCHAR; MQCHAR264 = array[Range264] of MQCHAR; TMQConnectionName = type MQCHAR264; TMQChannelName = type MQCHAR20; TMQQueueManagerName = type MQCHAR48; TMQQueueName = type MQCHAR48; const SYSTEM_DEFAULT_MODEL_QUEUE = 'SYSTEM.DEFAULT.MODEL.QUEUE'; // Default model queue. SYSTEM_DEFAULT_MODEL_QUEUE_Name: TMQQueueName = ('S','Y','S','T','E','M','.','D','E','F','A','U','L','T','.','M','O','D','E','L','.','Q','U','E','U','E', ' ',' ', // http://publib.boulder.ibm.com/infocenter/wmqv7/v7r0/index.jsp?topic=%2Fcom.ibm.mq.csqzaj.doc%2Fsc10300_.htm ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ',' ',' ',' ',' ',' ',' ',' ',' ',' '); function PadRight(const Value: AnsiString; const ResultLength: Integer): AnsiString; var Index: Integer; begin Result := Value; SetLength(Result, ResultLength); for Index := Length(Value)+1 to ResultLength do Result[Index] := ' '; end; function ToMQQueueName(const Value: AnsiString): TMQQueueName; var PaddedValue: AnsiString; ResultSize: Integer; begin ResultSize := SizeOf(Result); PaddedValue := PadRight(Value, ResultSize); Move(PaddedValue[1], Result, ResultSize); end; var S: AnsiString; QueueName: TMQQueueName; QueueManagerName: TMQQueueManagerName; QueueName48: MQCHAR48; begin try S := SYSTEM_DEFAULT_MODEL_QUEUE; S := SYSTEM_DEFAULT_MODEL_QUEUE_Name; QueueName := SYSTEM_DEFAULT_MODEL_QUEUE; QueueName := SYSTEM_DEFAULT_MODEL_QUEUE_Name; QueueManagerName := SYSTEM_DEFAULT_MODEL_QUEUE; QueueManagerName := SYSTEM_DEFAULT_MODEL_QUEUE_Name; // 68 QueueName48 := SYSTEM_DEFAULT_MODEL_QUEUE; QueueName48 := SYSTEM_DEFAULT_MODEL_QUEUE_Name; // 71 QueueManagerName := QueueName; // 73 QueueName := QueueManagerName; // 74 QueueName48 := QueueManagerName; // 76 QueueManagerName := QueueName48; // 77 S := QueueName; S := QueueManagerName; S := QueueName48; QueueName := ToMQQueueName(S); QueueName := S; // 85 QueueManagerName := S; // 86 QueueName48 := S; // 87 except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. [DCC Error] TypeIdentity.dpr(68): E2010 Incompatible types: 'TMQQueueManagerName' and 'TMQQueueName' [DCC Error] TypeIdentity.dpr(71): E2010 Incompatible types: 'MQCHAR48' and 'TMQQueueName' [DCC Error] TypeIdentity.dpr(73): E2010 Incompatible types: 'TMQQueueManagerName' and 'TMQQueueName' [DCC Error] TypeIdentity.dpr(74): E2010 Incompatible types: 'TMQQueueName' and 'TMQQueueManagerName' [DCC Error] TypeIdentity.dpr(76): E2010 Incompatible types: 'MQCHAR48' and 'TMQQueueManagerName' [DCC Error] TypeIdentity.dpr(77): E2010 Incompatible types: 'TMQQueueManagerName' and 'MQCHAR48' [DCC Error] TypeIdentity.dpr(85): E2010 Incompatible types: 'TMQQueueName' and 'AnsiString' [DCC Error] TypeIdentity.dpr(86): E2010 Incompatible types: 'TMQQueueManagerName' and 'AnsiString' [DCC Error] TypeIdentity.dpr(87): E2010 Incompatible types: 'MQCHAR48' and 'AnsiString'
–EOF–
Filed under: Delphi, Delphi 1, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi 3, Delphi 4, Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi x64, Delphi XE, Delphi XE2, Delphi XE3, Development, Software Development