【问题标题】:Strange behaviour of TypeInfo by anonymous methods匿名方法对 TypeInfo 的奇怪行为
【发布时间】:2018-10-01 16:42:11
【问题描述】:

对于需要泛型类型“族”的一段代码,我尝试使用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 也不是 guid。它有一组相同的 8 个字节的重复集,大部分为 00。例如 (0, 225, 48, 180, 0, 0, 0, 0, 0, 225, 48, 180, 0, 0, 0, 0)

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

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

【问题讨论】:

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


    【解决方案1】:

    这并没有什么不寻常的地方。

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

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

    How are anonymous methods implemented under the hood?

    IntfFlagsTIntfFlagsBase,它是 SetTIntfFlag 枚举值:

    TIntfFlag = (ifHasGuid, ifDispInterface, ifDispatch);
    

    ifHasGuid
    接口有一个 GUID(全局唯一标识符)。

    ifDisp接口
    是一个调度接口。

    如果调度
    可以发货。

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


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


    更新:显然,对于启用了方法信息的接口(@98​​7654352@ 指令)或表示匿名方法类型的接口确实存在其他标志,这些标志在 @987654353 中未表示@枚举。我已经为此提交了错误报告:

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

    在这种情况下,TIntfFlag(6) 是匿名方法的标志。

    来自Undocumented "Interface flag" for 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]

    【讨论】:

    • 该集合没有值 6,它包含一个值为 6 的元素,它的值为 64(根据检查员)。所以这个值是无效的。
    • @ToonKrijthe 对于TIntfFlagsBase 的数值为 64,TIntfFlags 需要有 7 个元素,而事实并非如此。我认为你误诊了这个问题。另外,无论如何您都不应该直接访问TI.TypeData,而应该将TI 传递给GetTypeData()
    • @RemyLebeau .TypeData 与传递给GetTypeData 相同,它是TTypeInfo 上的一个方法。他也没有误诊这个问题。匿名方法类型的 typeinfo 中似乎有一些垃圾数据(或一些未记录的标志)。
    • 幸运的是,这不是一个真正的问题。但我只是好奇为什么。现在我认为这种类型的数据是不可靠的。
    • 我已经更新了我的答案。 TIntfFlag 定义中确实缺少几个标志。我已向 Embarcadero 报告。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-21
    • 2023-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多