有没有办法从通用约束类型获取GUID?

时间:2018-07-16 19:31:25

标签: delphi generics rtti delphi-10.1-berlin

在下面的示例代码中,Run<T>()显示GUID IFooIFoo<T>接口的值:

type
  IBar = interface
    ['{992E6597-42F1-40F8-B678-C4A86864B030}']
  end;

  IFoo = interface
    ['{0C589AF8-5727-4EAA-BB41-6D51D70B9D35}']
  end;

  IFoo<T> = interface(IFoo)
    ['{8FF54F6B-0896-4EA3-85F8-66BA70F9D2DA}']
  end;

  TTest = class
  public
    class procedure Run<T: IFoo>;
  end;

class procedure TTest.Run<T>;
var
  LContext: TRttiContext;
  IFoo_T_TypeInfo: PTypeInfo;
  IFooTypeInfo: PTypeInfo;
begin
  IFoo_T_TypeInfo := TypeInfo(T);
  IFooTypeInfo    := LContext.GetType(TypeInfo(T)).BaseType.Handle;

  WriteLn('IFoo<T> GUID: ', GetTypeData(IFoo_T_TypeInfo).GUID.ToString);
  WriteLn('IFoo    GUID: ', GetTypeData(IFooTypeInfo).GUID.ToString);
  WriteLn('IBar    GUID: ', '?');
end;

begin
  TTest.Run<IFoo<IBar>>;
  ReadLn;
end.

是否可以从通用约束类型TGUID接口中获取PTypeInfoIBar

P.S .:我不想将Run<T>()的签名更改为Run<T, U>(),只是从美国获得IBar GUID。

1 个答案:

答案 0 :(得分:3)

从泛型类型参数获取typeinfo / RTTI有点棘手,但并非完全不可能。

这是一些示例代码(我正在使用Spring.Reflections单元中的RTTI扩展)。

uses
  Rtti,
  SysUtils,
  Spring.Reflection;

type
  TTest = class
  public
    class procedure Run<T: IFoo>;
  end;

class procedure TTest.Run<T>;
var
  LType, LType2: TRttiType;
begin
  LType := TType.GetType(TypeInfo(T));
  if LType.IsInterface then
  begin
    if LType.AsInterface.HasGuid then
      Writeln(LType.Name, ' GUID: ', LType.AsInterface.GUID.ToString);
    LType2 := LType.BaseType;
    while Assigned(LType2) and (LType2.Handle <> TypeInfo(IInterface)) do
    begin
      if LType2.AsInterface.HasGuid then
        Writeln(LType2.Name, ' GUID: ', LType2.AsInterface.GUID.ToString);
      LType2 := LType2.BaseType;
    end;

    if LType.IsGenericType then
    begin
      for LType2 in LType.GetGenericArguments do
        if Assigned(LType2) and LType2.IsInterface then
          Writeln(LType2.Name, ' GUID: ', LType2.AsInterface.GUID.ToString);
    end;
  end
end;

var
  bar: IBar;
begin
  bar := TBar.Create; // cause RTTI for IBar to be generated to look it up later
  TTest.Run<IFoo<IBar>>;
  ReadLn;
end.

通过类型名称的字符串解析来检查类型是否为泛型。如果包含尖括号,则为通用类型。然后提取类型名称,这些类型名称始终是完全合格的类型名称,从而可以查找它们。

但是要记住一个陷阱。您只能在非通用类型参数的其他上下文中生成该类型的类型信息时才查找这些类型。这就是为什么在该示例中,我制作了一个简单的TBar类,该类实现了IBar并创建了一些实例以防止链接程序剥离该类(以及必需的RTTI)。在实际代码中,这不是问题,因为您通常具有该接口的某些实现。同样,为了使该示例正常工作,您还需要将接口放在自己的单元中,因为按完全限定名查找不适用于dpr中的类型。