【问题标题】:Delphi Accessing properties of an interface as a generic typeDelphi 将接口的属性作为泛型类型访问
【发布时间】:2020-05-18 04:10:08
【问题描述】:

我正在尝试通过可以采用任何类型的通用类中的接口访问基础对象的属性,即对参数没有约束。我可以为类参数成功执行此操作,但当参数是接口时遇到问题。通过阅读这里的许多帖子,我得出的结论是,访问接口的 RTTI 数据的唯一方法是转换回 TObject(假设它来自一个在我的上下文中是安全的对象)。

我的想法是使用支持来提取接口,然后将其转换回 TObject,然后测试 RTTI 以获取属性信息。我的问题是 support 函数正在寻找 TObject、IInterface 或 TClass 的实例,如果没有参数约束,我无法从泛型类型 T 中获得它,这违背了包含此方法的类的目的。我生成了以下代码,显示了我对解决方案的尝试。

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.RTTI,
  System.Typinfo;

Type
  ITestIntf = interface(IInvokable)
    ['{61E0E2FC-59FA-4300-BE84-068BC265376E}']
    function GetData: string;
    procedure SetData(const Value: string);
    property Data : string read GetData write SetData;
  end;

  TTestClass=class(TInterfacedObject, ITestIntf)
  private
    FData : string;
    function GetData: string;
    procedure SetData(const Value: string);
  public
    property Data : string read GetData write SetData;
  end;

  TAccess<T> = class
    Function CheckProperty(AInstance : T; APropName : string) : boolean;
  end;

var
  LAccIntf : TAccess<ITestIntf>;
  LAccClass : TAccess<TTestClass>;
  LTestCls : TTestClass;
  LTestIntf : ITestIntf;

{ TTestClass }

function TTestClass.GetData: string;
begin
  Result := FData;
end;

procedure TTestClass.SetData(const Value: string);
begin
  FData := Value;
end;

{ TAccess<T> }

function TAccess<T>.CheckProperty(AInstance : T; APropName : string) : boolean;

var
  LType, LInstType : TRTTIType;
  LIntf : IInterface;
  LGUID : TGUID;
  LContext : TRttiContext;
  LInstance : TObject;

begin
  Result := false;
  LType := LContext.GetType(Typeinfo(T));
  if LType.TypeKind = tkInterface then
  begin
    LGUID := GetTypeData(TypeInfo(T))^.Guid;
    If Supports(AInstance, LGUID, LIntf) then // <- Error here
    begin
      LInstance := LIntf as TObject;
      LInstType := LContext.GetType(LInstance.ClassType);
      Result := (LInstType.GetProperty(APropName) <> nil);
    end;
  end else
  if LType.TypeKind = tkClass then
  begin
    Result := (LType.GetProperty(APropName) <> nil);
  end;
end;

begin
  try
    LTestCls := TTestClass.create;
    LTestIntf := TTestClass.create;
    LAccClass := TAccess<TTestClass>.Create;
    if LAccClass.CheckProperty(LTestCls,'Data') then
      writeln('Class Success!')
    else
      writeln('Class Failed');

    LAccIntf := TAccess<ITestIntf>.Create;
    if LAccIntf.CheckProperty(LTestIntf, 'Data') then
      writeln('Intf Success!')
    else
      writeln('Intf Failed');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  readln;
end. 

编译时出现错误:

[dcc32 Error] Project1.dpr(68): E2250 There is no overloaded version of 'Supports' that can be called with these arguments

我不确定这是否可行,或者如果可以,我是否以正确的方式进行?

谢谢

【问题讨论】:

  • 编译器错误是因为编译器不知道AInstance是一个接口。您可以将其转换为,从而克服障碍。这仍然不一定能解决您的问题。接口属性不需要由实现对象实现。只需要实现 getter 和 setter 方法。甚至它们也不需要使用与接口中相同的名称来实现。我认为您在这里尝试做的事情是有缺陷的。
  • 感谢@David 的回复。诚然,我们将接口用于在此实例中主要传输数据的对象,而更多用于垃圾收集,然后是其他任何东西,因此可能不是接口的最佳用途。话虽如此,您将如何进行评论中提到的演员阵容?

标签: delphi generics interface delphi-10.2-tokyo


【解决方案1】:

您的编译器反对此代码,因为它AInstance 在编译时不受限制为接口变量。您的代码会在运行时对其进行测试,因此可以安全地转换 AInstance

有多种方法可以做到这一点。我可能会这样做:

Supports(PInterface(@AInstance)^, LGUID, LIntf)

在哪里

PInterface = ^IInterface

请注意,虽然这将使您的代码按您的意愿编译和运行,但它实际上并没有测试接口是否实现了属性。例如,如果您从实现类定义中删除 Data 属性,则接口确实实现了 Data 属性,但您的函数返回 False

【讨论】:

  • 感谢您的回答,我实际上并没有意识到在接口中使用属性的微妙之处。鉴于您的 cmets,我可能只是恢复使用标准类进行数据传输并相应地处理垃圾收集。
猜你喜欢
  • 2011-09-24
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多