Delphi作为通用类型访问接口的属性

问题描述 投票:1回答:1

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

我的想法是使用支持来提取接口,然后将其投射回TObject,然后测试RTTI以获取属性信息。我的问题是,supports函数正在寻找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

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

谢谢

delphi generics interface delphi-10.2-tokyo
1个回答
0
投票

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

有多种方法可以执行此操作。我可能会这样:

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

其中

PInterface = ^IInterface

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

© www.soinside.com 2019 - 2024. All rights reserved.