There is nothing really unusual about this.
An anonymous method is implemented as a compiler-generated interface that has an Invoke()
method matching the same signature as the anonymous method. That is why the TTypeKind
is tkInterface
and the IntfParent
is IInterface
.
Behind the interface is a compiler-generated implementation class that contains the captured variables, and the body of the anonymous method within its Invoke()
implementation.
How are anonymous methods implemented under the hood?
The IntfFlags
is a TIntfFlagsBase
, which is a Set
of TIntfFlag
enum values:
TIntfFlag = (ifHasGuid, ifDispInterface, ifDispatch);
ifHasGuid
Interface has a GUID (Globally Unique Identifier).
ifDispInterface
Is a dispatch interface.
ifDispatch
Can be dispatched.
A Set
is a bitmask of values. Each enum value is represented by a specific bit in the mask. Within TIntfFlagsBase
, ifHasGuid
is bit 0, ifDispInterface
is bit 1, and ifDispatch
is bit 2. So, a numeric value of 6 (110b
) would be the ifDispInterface
and ifDispatch
flags enabled, but not the ifHasGuid
flag. As such, the IntfGuid
has no meaningful value, but still takes up space in the TTypeData
for alignment purposes.
Update: I tested with XE2, and sure enough, I see IntfFlags
is set to ordinal 64 (TIntfFlag(6)
, like you see) instead of ordinal 6, as expected. The only difference between what I see and what you see is that I see the Guid
is completely empty (all zeros).
Update: apparently, there are indeed additional flags present for interfaces that have method info enabled (the {$M+}
directive), or that represent anonymous method types, which are not represented in the TIntfFlag
enum. I have filed a bug report for that:
RSP-24631: System.TypInfo.TIntfFlag enum is missing flags
In this case, TIntfFlag(6)
is the flag for anonymous methods.
From Undocumented "Interface flag" for IInvokable?:
It seems indeed as if the TIntfFlag enum was never extended since Delphi6 (I think that was when interface RTTI was introduced) - I can confirm that at least since XE an interface type with $M+ gets a fourth flag (lets call it ifHasMethodInfo) set.
If the type is an anonymous method type ... then there is a 7th enum value in the set. The situations where bit 5 and 6 are set are unknown to me.
...
I can confirm my findings with this code:
uses
SysUtils,
Rtti;
type
TIntfFlagEx = (ifHasGuid, ifDispInterface, ifDispatch, ifMethodInfo, ifUnknown, ifUnknown2, ifAnonymousMethod);
TIntfFlagsEx = set of TIntfFlagEx;
{$M+}
IFoo = interface
['{35CFB4E2-4A13-48E9-8026-C1558001F4B7}']
procedure Main;
end;
{$M-}
{$M+}
IBar = interface(TProc)
['{AB2FEC1A-339F-4E58-B3DB-EC7B734F461B}']
end;
{$M-}
{$M+}
TMyProc = reference to procedure;
{$M-}
procedure PrintIntf(typeInfo: Pointer);
var
context: TRttiContext;
rttiInterface: TRttiInterfaceType;
flags: TIntfFlagsEx;
begin
rttiInterface := context.GetType(typeInfo) as TRttiInterfaceType;
flags := TIntfFlagsEx(rttiInterface.IntfFlags);
Writeln(rttiInterface.Name, ' ', TValue.From(flags).ToString);
end;
begin
PrintIntf(TypeInfo(IInterface));
PrintIntf(TypeInfo(IInvokable));
PrintIntf(TypeInfo(IFoo));
PrintIntf(TypeInfo(TProc));
PrintIntf(TypeInfo(TFunc<Integer>));
PrintIntf(TypeInfo(TMyProc));
PrintIntf(TypeInfo(IBar));
Readln;
end.
prints this:
IInterface [ifHasGuid]
IInvokable [ifMethodInfo]
IFoo [ifHasGuid,ifMethodInfo]
TProc [ifAnonymousMethod]
TFunc [ifAnonymousMethod]
TMyProc [ifMethodInfo,ifAnonymousMethod]
IBar [ifHasGuid,ifMethodInfo,ifAnonymousMethod]