简体   繁体   English

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

[英]Delphi Accessing properties of an interface as a generic type

I am trying to access the properties of an underlying object via an interface in a generic class that can take any type, ie NO constraints on the parameter.我正在尝试通过通用 class 中的接口访问底层 object 的属性,该接口可以采用任何类型,即对参数没有限制。 I can do this successfully for a class parameter but having trouble when the parameter is an interface.我可以为 class 参数成功执行此操作,但在参数为接口时遇到问题。 From reading many posts here I have come to the conclusion that the only way to access the RTTI data for an interface is casting back to a TObject (assumption is it came from an object to start with which is safe in my context).通过阅读这里的许多帖子,我得出的结论是,访问接口的 RTTI 数据的唯一方法是转换回 TObject(假设它来自 object 开始,在我的上下文中是安全的)。

My thought was to use supports to extract the interface then cast it back to a TObject and then test the RTTI to get the property information.我的想法是使用支持来提取接口,然后将其转换回 TObject,然后测试 RTTI 以获取属性信息。 My problem is that the supports function is looking for an instance of TObject, IInterface or TClass which I cannot get from the generic type T without a parameter constraint which defeats the purpose of the class that contains this method.我的问题是支持 function 正在寻找 TObject、IInterface 或 TClass 的实例,如果没有参数约束,我无法从泛型类型 T 中获得它,这违背了包含此方法的 class 的目的。 I have produced the following code that shows my attempt at a solution.我已经生成了以下代码,显示了我对解决方案的尝试。

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. 

When I compile this I get an error:当我编译这个我得到一个错误:

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

I am not sure this is possible or if it is, am I going about it the right way?我不确定这是可能的,或者如果是的话,我是否以正确的方式进行?

Thanks谢谢

Your compiler objects to this code because it AInstance is not constrained at compile time to be an interface variable.您的编译器反对此代码,因为它AInstance在编译时不受约束为接口变量。 Your code tests for that at runtime, so it is safe to cast AInstance .您的代码会在运行时对此进行测试,因此可以安全地AInstance

There are a variety of ways to do this.有多种方法可以做到这一点。 I'd probably do it like this:我可能会这样做:

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

where在哪里

PInterface = ^IInterface

Note that whilst this will make your code compile and run as you intend, it doesn't actually test whether the interface implements a property.请注意,虽然这将使您的代码按您的意图编译和运行,但它实际上并没有测试接口是否实现了属性。 For instance, if you remove the Data property from the implementing class definition, then the interface does indeed implement the Data property, but your function returns False .例如,如果您从实现 class 定义中删除Data属性,则该接口确实实现了Data属性,但您的 function 返回False

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM