delphi - 匿名方法的 TypeInfo 的奇怪行为

标签 delphi anonymous-methods delphi-10.2-tokyo typeinfo

对于需要泛型类型“系列”的一段代码,我尝试使用 TypeInfo 来检索所需的信息。

class function GetTypeKind<T>:TTypeKind;

对于大多数类型我都能弄清楚。但匿名方法类型的行为却出乎意料。

我有一个匿名方法类型定义为:

TMethodProc = reference to procedure;

我尝试获取类型信息:

MyKind := GetTypeKind<TMethodProc>;

class function GetTypeKind<T>:TTypeKind;
var 
  TI: PTypeInfo;
begin
  TI := TypeInfo(T);

  ...
end;

我知道匿名方法背后有一些编译器魔法。但我得到以下结果:

TI.TypeData.IntfParent == IInterface
TI.TypeData.IntfFlags == [(out of bounds)6]

标志具有意外值,TIntfFlag 有三个值,因此 6 是意外值。 GUID 也不是指南。它具有相同的 8 个字节的重复集合,大部分为 00。例如 (0, 225, 48, 180, 0, 0, 0, 0, 0, 225, 48, 180, 0, 0, 0, 0)

匿名方法是否从 TypeInfo 中排除,或者经过一些调整是否有用。

此外,(奇怪的)6 是一个未记录的功能,还是可以是任何值?

最佳答案

这没什么不寻常的。

匿名方法作为编译器生成的接口(interface)实现,该接口(interface)具有与匿名方法匹配相同签名的 Invoke() 方法。这就是为什么 TTypeKindtkInterfaceIntfParentIInterface

接口(interface)后面是编译器生成的实现类,其中包含捕获的变量以及其 Invoke() 实现中的匿名方法的主体。

How are anonymous methods implemented under the hood?

IntfFlagsTIntfFlagsBase ,这是 SetTIntfFlag枚举值:

TIntfFlag = (ifHasGuid, ifDispInterface, ifDispatch);

ifHasGuid
Interface has a GUID (Globally Unique Identifier).

ifDispInterface
Is a dispatch interface.

ifDispatch
Can be dispatched.

Set 是值的位掩码。每个枚举值都由掩码中的特定位表示。在 TIntfFlagsBase 中,ifHasGuid 是位 0,ifDispInterface 是位 1,ifDispatch 是位 2。数值 6 (110b) 将启用 ifDispInterfaceifDispatch 标志,但不会启用 ifHasGuid 标志。因此,IntfGuid 没有任何有意义的值,但仍占用 TTypeData 中的空间以进行对齐。

<小时/>

更新:我使用 XE2 进行了测试,果然,我看到 IntfFlags 设置为序数 64(TIntfFlag(6),例如你看)而不是预期的序数 6。我所看到的和您所看到的唯一区别是我看到的 Guid 完全是空的(全是零)。

<小时/>

更新:显然,对于启用了方法信息的接口(interface)({$M+}指令)或表示匿名方法类型,确实存在额外的标志,未在 TIntfFlag 枚举中表示。我已为此提交了错误报告:

RSP-24631: System.TypInfo.TIntfFlag enum is missing flags

在本例中,TIntfFlag(6) 是匿名方法的标志。

来自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]

关于delphi - 匿名方法的 TypeInfo 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49950534/

相关文章:

c# - 是什么让这个计时器在范围内?匿名方法?

c# - 这还是闭包吗?

delphi - 如何将 FMX.TImage 分配给 VCL.TBitmap?

delphi - 如何让 IDE 知道我使用了祖先变量?

delphi - TFDMoniFlatFileClientLink 不规则地不跟踪到文件

delphi - Delphi xe5 中文件日期返回错误

delphi - Delphi 1 的轻量级数据库引擎?

ios - Firemonkey iOS TExpanderButton onclick

vb.net - 使用 vb.net Action(Of T) 和 lambda 声明匿名方法时出现问题

delphi - 将 VirtualTreeView 编辑器移动到第二列