Delphi 带有接口类型约束的泛型类型的RTTI

Delphi 带有接口类型约束的泛型类型的RTTI,delphi,generics,interface,rtti,Delphi,Generics,Interface,Rtti,是否可以检查带有接口类型约束的泛型类型实例的RTTI信息?这个问题可能有点模棱两可,所以我创建了一个示例控制台应用程序来展示我正在尝试做什么: program Project3; {$APPTYPE CONSOLE} uses RTTI, SysUtils, TypInfo; type TMyAttribute = class(TCustomAttribute) strict private FName: string; public constru

是否可以检查带有接口类型约束的泛型类型实例的RTTI信息?这个问题可能有点模棱两可,所以我创建了一个示例控制台应用程序来展示我正在尝试做什么:

program Project3;

{$APPTYPE CONSOLE}

uses
  RTTI,
  SysUtils,
  TypInfo;

type
  TMyAttribute = class(TCustomAttribute)
  strict private
    FName: string;
  public
    constructor Create(AName: string);
    property Name: string read FName;
  end;

  IMyObjectBase = interface
  ['{E063AD44-B7F1-443C-B9FE-AEB7395B39DE}']
    procedure DoSomething;
  end;

  TMyObjectBase = class(TInterfacedObject, IMyObjectBase)
  public
    procedure DoSomething; virtual;
  end;

  [TMyAttribute('First')]
  TMyFirstRealClass = class(TMyObjectBase)
  public
    procedure DoSomethingDifferent;
  end;

  [TMyAttribute('Second')]
  TMySecondRealClass = class(TMyObjectBase)
  public
    procedure BeSomethingDifferent;
  end;

  TGenericClass<I: IMyObjectBase> = class
  public
    function GetAttributeName(AObject: I): string;
  end;


{ TMyAttribute }

constructor TMyAttribute.Create(AName: string);
begin
  FName := AName;
end;

{ TMyObjectBase }

procedure TMyObjectBase.DoSomething;
begin
end;

{ TMyFirstRealClass }

procedure TMyFirstRealClass.DoSomethingDifferent;
begin
end;

{ TMySecondRealClass }

procedure TMySecondRealClass.BeSomethingDifferent;
begin
end;

{ TGenericClass<I> }

function TGenericClass<I>.GetAttributeName(AObject: I): string;
var
  LContext: TRttiContext;
  LProp: TRttiProperty;
  LAttr: TCustomAttribute;
begin
  Result := '';
  LContext := TRttiContext.Create;
  try
    for LAttr in LContext.GetType(AObject).GetAttributes do
    // ----> [DCC Error] E2250 There is no overloaded version of 'GetType' that can be called with these arguments
      if LAttr is TMyAttribute then
      begin
        Result := TMyAttribute(LAttr).Name;
        Break;
      end;
  finally
    LContext.Free;
  end;
end;

var
  LFirstObject: IMyObjectBase;
  LSecondObject: IMyObjectBase;
  LGeneric: TGenericClass<IMyObjectBase>;
begin
  try
    LFirstObject := TMyFirstRealClass.Create;
    LSecondObject := TMySecondRealClass.Create;

    LGeneric := TGenericClass<IMyObjectBase>.Create;

    Writeln(LGeneric.GetAttributeName(LFirstObject));
    Writeln(LGeneric.GetAttributeName(LSecondObject));

    LGeneric.Free;

    LFirstObject := nil;
    LSecondObject := nil;

    Readln;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
程序项目3;
{$APPTYPE控制台}
使用
RTTI,
SysUtils,
TypInfo;
类型
TMyAttribute=class(TCustomAttribute)
严格保密
FName:字符串;
公众的
构造函数创建(AName:string);
属性名称:字符串读取FName;
结束;
IMyObjectBase=接口
[{E063AD44-B7F1-443C-B9FE-AEB7395B39DE}]
程序剂量测定;
结束;
TMyObjectBase=class(TInterfacedObject、IMyObjectBase)
公众的
程序剂量测定;事实上的
结束;
[TMyAttribute('First')]
TMyFirstRealClass=类(TMyObjectBase)
公众的
程序剂量不同;
结束;
[TMyAttribute(‘第二’)]
TMySecondRealClass=类(TMyObjectBase)
公众的
程序不同;
结束;
TGenericClass=class
公众的
函数GetAttributeName(AOObject:I):字符串;
结束;
{TMyAttribute}
构造函数TMyAttribute.Create(AName:string);
开始
FName:=AName;
结束;
{TMyObjectBase}
程序TMyObjectBase.DoSomething;
开始
结束;
{tmyFirsteralClass}
程序TMyFirstRealClass.dosomething不同;
开始
结束;
{TMySecondRealClass}
程序TMySecondRealClass.BeSomethingDifferent;
开始
结束;
{TGenericClass}
函数TGenericClass.GetAttributeName(AOObject:I):字符串;
变量
LContext:trttitcontext;
LProp:trtti属性;
LAttr:TCustomAttribute;
开始
结果:='';
LContext:=TRttiContext.Create;
尝试
对于LContext.GetType(AOObject.GetAttributes)中的LAttr
//--->[DCC Error]E2250没有可以使用这些参数调用的“GetType”重载版本
如果LAttr是TMyAttribute,则
开始
结果:=TMyAttribute(LAttr).Name;
打破
结束;
最后
LContext.Free;
结束;
结束;
变量
LFirstObject:IMyObjectBase;
LSecondObject:IMyObjectBase;
LGeneric:TGenericClass;
开始
尝试
LFirstObject:=TMyFirstRealClass.Create;
LSecondObject:=TMySecondRealClass.Create;
LGeneric:=TGenericClass.Create;
Writeln(LGeneric.GetAttributeName(LFirstObject));
Writeln(LGeneric.GetAttributeName(LSecondObject));
LGeneric.自由;
LFirstObject:=nil;
LSecondObject:=nil;
Readln;
除了
关于E:Exception-do
Writeln(E.ClassName,“:”,E.Message);
结束;
结束。
我需要检查传入的对象(AOObject),而不是通用接口(I)。 (Dephi 2010)。
感谢您的建议。

两种可能的解决方案如下:

1) 我用它进行了测试,它可以工作(XE4):

2) 我用它进行了测试,它可以工作(XE4):

3) 在返回对象的接口上创建方法,并使用该方法检查对象:

IMyObjectBase = interface
['{E063AD44-B7F1-443C-B9FE-AEB7395B39DE}']
  procedure DoSomething;
  function GetObject: TObject;
end;

TMyObjectBase = class(TInterfacedObject, IMyObjectBase)
public
  procedure DoSomething; virtual;
  function GetObject: TObject;
end;

{ TMyObjectBase }

function TMyObjectBase.GetObject: TObject;
begin
  Result := Self;
end;
然后这样称呼它:

for LAttr in LContext.GetType(AObject.GetObject.ClassType).GetAttributes do

您是否尝试过像“在LContext.GetType(TObject(AObject)).GetAttributes中为LAttr执行”这样的操作?对象强制转换的接口是在Delphi2010中添加的。这不是我喜欢做的事情,但在这种情况下它会有所帮助。是的,这似乎是一个显而易见的解决方案,但它说的是“无效的类型转换”。好的,测试过了,它不起作用。现在将编辑我的答案以删除该选项。您必须使用
as
操作符将接口强制转换为对象,例如:LContext.GetType((AObject as TObject.ClassType).GetAttributes中的LAttr的
。阅读文档:您可能希望更改标题,因为这不是泛型接口(
IMyObjectBase
将是泛型接口),而是泛型类型上的类型参数,并对接口类型进行了约束。这可以通过以下小修改来实现。。。我需要将“.ClassType”添加到以下内容:对于LContext.GetType(AObject.GetObject.ClassType)中的LAttr。我不确定这是否是一种“纯粹主义”的方式,但它仍然有效。@RickWheeler对TMyObjectBase的铸造似乎在表面上也有效,因此我更新了答案以反映这一点。正如您所说,“纯粹主义者”可能想跟我说一句话:)您不需要让接口公开一个方法来检索实现对象。您可以直接将接口强制转换为
TObject
用于LContext.GetType((AObject作为TObject.ClassType)中的LAttr。GetAttributes做
@RemyLebeau谢谢,我用硬转换进行了测试,但失败了,所以我假设软转换也会失败。将添加该选项作为第三个选项。你知道为什么软强制转换有效而硬强制转换被编译器抛出吗?@RickWheeler在Delphi2010中,你需要更多地帮助编译器。您必须将
i接口(AObject)编写为TObject
TObject(i接口(AObject))
IMyObjectBase = interface
['{E063AD44-B7F1-443C-B9FE-AEB7395B39DE}']
  procedure DoSomething;
  function GetObject: TObject;
end;

TMyObjectBase = class(TInterfacedObject, IMyObjectBase)
public
  procedure DoSomething; virtual;
  function GetObject: TObject;
end;

{ TMyObjectBase }

function TMyObjectBase.GetObject: TObject;
begin
  Result := Self;
end;
for LAttr in LContext.GetType(AObject.GetObject.ClassType).GetAttributes do