На самом деле в этом нет ничего необычного.
Анонимный метод реализован как интерфейс, сгенерированный компилятором, который имеет метод Invoke()
, соответствующий той же сигнатуре, что и анонимный метод. Вот почему TTypeKind
это tkInterface
, а IntfParent
это IInterface
.
За интерфейсом находится сгенерированный компилятором класс реализации, который содержит захваченные переменные и тело анонимного метода в его Invoke()
реализации.
Как внутри реализованы анонимные методы?
IntfFlags
— это TIntfFlagsBase
, который является Set
из TIntfFlag
значения перечисления:
TIntfFlag = (ifHasGuid, ifDispInterface, ifDispatch);
Интерфейс ifHasGuid
имеет GUID (глобальный уникальный идентификатор).
ifDispInterface
Интерфейс диспетчеризации.
ifDispatch
Может быть отправлен.
Set
— это битовая маска значений. Каждое значение перечисления представлено определенным битом в маске. В пределах TIntfFlagsBase
ifHasGuid
— это бит 0, ifDispInterface
— это бит 1, а ifDispatch
— это бит 2. Таким образом, числовое значение 6 (110b
) будет включать флаги ifDispInterface
и ifDispatch
, но не флаг ifHasGuid
. Таким образом, IntfGuid
не имеет значимого значения, но все же занимает место в TTypeData
для целей выравнивания.
Обновление: я тестировал с XE2 и, конечно же, вижу, что IntfFlags
установлен на порядковый номер 64 (TIntfFlag(6)
, как вы видите) вместо порядкового номера 6, как ожидалось. Единственная разница между тем, что вижу я, и тем, что видите вы, заключается в том, что я вижу, что Guid
совершенно пусто (все нули).
Обновление: по-видимому, действительно существуют дополнительные флаги для интерфейсов, для которых включена информация о методах (директива {$M+}
), или которые представляют анонимные типы методов, которые не представлены в перечислении TIntfFlag
. Я подал отчет об ошибке для этого:
RSP-24631: в перечислении System.TypInfo.TIntfFlag отсутствуют флаги
В данном случае TIntfFlag(6)
— это флаг анонимных методов.
Из Недокументированный "флаг интерфейса" для IInvokable? :
Действительно кажется, что перечисление TIntfFlag никогда не расширялось со времен Delphi6 (я думаю, что это было, когда был введен интерфейс RTTI) - я могу подтвердить, что, по крайней мере, с XE тип интерфейса с $M+ получает четвертый флаг (назовем его ifHasMethodInfo) установленным .
Если тип является типом анонимного метода... тогда в наборе есть 7-е значение перечисления. Ситуации, когда бит 5 и 6 установлены, мне неизвестны.
...
Я могу подтвердить свои выводы с помощью этого кода:
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.
печатает это:
IInterface [ifHasGuid]
IInvokable [ifMethodInfo]
IFoo [ifHasGuid,ifMethodInfo]
TProc [ifAnonymousMethod]
TFunc [ifAnonymousMethod]
TMyProc [ifMethodInfo,ifAnonymousMethod]
IBar [ifHasGuid,ifMethodInfo,ifAnonymousMethod]
person
Remy Lebeau
schedule
21.04.2018