【发布时间】: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