我正在尝试通过通用类中的接口访问基础对象的属性,该类可以采用任何类型,即对参数没有约束。我可以为类参数成功完成此操作,但是当参数是接口时遇到麻烦。通过阅读这里的许多文章,我得出的结论是,访问接口的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
我不确定这是否可行,或者我是否会以正确的方式进行?
谢谢
您的编译器反对此代码,因为在编译时未将其AInstance
限制为接口变量。您的代码会在运行时对此进行测试,因此可以安全地强制转换为AInstance
。
有多种方法可以执行此操作。我可能会这样:
Supports(PInterface(@AInstance)^, LGUID, LIntf)
其中
PInterface = ^IInterface
[请注意,虽然这将使您的代码按预期进行编译和运行,但实际上并没有测试接口是否实现了属性。例如,如果从实现的类定义中删除Data
属性,则该接口确实实现了Data
属性,但是您的函数返回False
。