Howdey,
我正在使用TVirtualInterface来实现一些接口。这些interfaes代表可以在DB中找到的密钥。我使用自定义代码生成器生成接口定义。例如:
// Base code
IKey = interface
function KeyFields : string;
function KeyValues : Variant;
function GetKeyValue(const aKeyName : string) : Variant;
procedure SetKeyValue(const aKeyName : string; Value : Variant);
end;
// Generated code
ITable1Key = interface(IKey)
end;
ITable1Key1 = interface(ITable1Key)
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
end;
ITable1Key2 = interface(ITable1Key)
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
procedure SetField2(const Value : string);
function GetField2 : string;
property Field2 : string read GetField1 write SetField1;
end;
// Other generated declarations
我使用TVirtualInterface来实现每个IKey接口,而不是逐个实现它们。
虽然,在我的TVirtualInterface中:
TKey = TVirtualInterface
public
constructor Create(aType : PTypeInfo);
function Cast : IKey;
end;
TKey<T : IKey>
public
constructor Create; reintroduce;
function Cast : T;
end;
constructor TKey.Create(aType : PTypeInfo)
begin
inherited Create(aType, aHandlerMethod);
end;
function TKey.Cast;
var
pInfo: PTypeInfo;
begin
pInfo := TypeInfo(IKey);
if QueryInterface(GetTypeData(pInfo).Guid, Result) <> 0 then
begin
raise Exception.CreateFmt('Sorry, TKey is unable to cast %s to its interface ', [string(pInfo.Name)]);
end;
end;
constructor TKey<T>.Create;
begin
inherited Create(TypeInfo(T));
end;
function TKey<T>.Cast;
var
pInfo: PTypeInfo;
begin
pInfo := TypeInfo(T);
if QueryInterface(GetTypeData(pInfo).Guid, Result) <> 0 then
begin
raise Exception.CreateFmt('Sorry, TKey<T> is unable to cast %s to its interface ', [string(pInfo.Name)]);
end;
end;
使用TKey.Cast方法将TKey虚拟接口转换为T类型没有问题,但TKey.Cast返回接口不支持错误。
我在System.Rtti中检查了没有按照我想要的方式工作的部分:
function TVirtualInterface.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if iid = FIID then
begin
_AddRef;
Pointer(Obj) := @VTable;
Result := S_OK;
end
else
Result := inherited
end;
现在,我如何强制TVirtualInterface将自身强制转换为FIID字段的父接口的IID?我是否必须为IKey接口创建另一个TVirtualInterface实例?
非常感谢。
答案 0 :(得分:2)
您滥用TVirtualInterface
。它只是一个RTTI助手,你根本不应该从中得到它。您应该从TInterfacedObject
派生而来。
此外,您的TKey
个类都忽略了传递给构造函数的PTypeInfo
。非通用TKey.Cast()
始终仅查询IKey
,而不是后代接口。通用TKey<T>.Cast
始终会重新查询T
的RTTI以获取其IID。所以摆脱构造函数中的PTypeInfo
,就浪费了。
由于非Generic TKey
只是一个根本没有实际实现任何派生接口的基类,TKey.QueryInterface()
总是会因IKey
以外的任何接口而失败本身。至少Generic TKey
可以查询派生接口。
您的Cast
函数无论如何都是多余的,因为您可以使用as
运算符或SysUtils.Supports()
函数将一个接口转换为另一个接口。这些是首选方法,不是手动使用QueryInterface()
。
在任何情况下,您的接口都在其声明中缺少IID,因此无论如何都无法在接口之间进行转换。
尝试更像这样的事情:
// Base code
IKey = interface
['{D6D212E0-C173-468C-8267-962CFC3FECF5}']
function KeyFields : string;
function KeyValues : Variant;
function GetKeyValue(const aKeyName : string) : Variant;
procedure SetKeyValue(const aKeyName : string; Value : Variant);
end;
// Generated code
ITable1Key = interface(IKey)
['{B8E44C43-7248-442C-AE1B-6B9E426372C1}']
end;
ITable1Key1 = interface(ITable1Key)
['{0C86ECAA-A8E7-49EB-834F-77DE62BE1D28}']
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
end;
ITable1Key2 = interface(ITable1Key)
['{82226DE9-221C-4268-B971-CD72617C19C7}']
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
procedure SetField2(const Value : string);
function GetField2 : string;
property Field2 : string read GetField1 write SetField1;
end;
// Other generated declarations
type
TKey = class(TInterfacedObject, IKey)
public
function Cast : IKey;
// IKey methods...
end;
TKey<T : IKey> = class(TInterfacedObject, IKey, T)
public
function Cast : T;
end;
TTable1Key = class(TKey, IKey, ITable1Key)
end;
TTable1Key1 = class(TTable1Key, IKey, ITable1Key, ITable1Key1)
public
// ITable1Key1 methods...
end;
TTable1Key2 = class(TTable1Key, IKey, ITable1Key, ITable1Key2)
public
// Table1Key2 methods...
end;
// and so on ...
function TKey.Cast: IKey;
begin
if not Supports(Self, IKey, Result) then
raise Exception.Create('Sorry, unable to cast to IKey');
end;
function TKey<T>.Cast: T;
begin
if not Supports(Self, GetTypeData(TypeInfo(T)).Guid, Result) then
raise Exception.CreateFmt('Sorry, unable to cast to %s', [string(TypeInfo(T).Name)]);
end;
// other class methods as needed ...
还要注意派生类如何重复其基类实现的接口。这是Delphi的一个已知限制。派生类不继承基类接口。每个类都必须显式指定它实现的接口,即使实际的实现是在基类中。