Delphi 在对具体接口类型的引用上调用函数时,调用了我的通用接口的错误函数
Delphi calls the incorrect function of my generic interface, when calling the function on a reference to the the concrete interface type
我有一个实现通用接口多个版本的对象。为避免混淆,我在 class 中使用了方法解析子句:
https://docwiki.embarcadero.com/RADStudio/Sydney/en/Implementing_Interfaces
假设我有一个多动物处理程序,可以处理猫和狗(无论那是什么意思)。处理程序为猫和狗实现处理程序 (IRequestHandler<TResponse, TRequest>
) 接口:
IRequestHandler<TResponse: record; TRequest: record> = Interface(IInvokable)
['{AFF8703B-F2AC-44D6-B82C-43E3492ADBB3}']
function Handle(ARequest: TRequest): TResponse;
End;
TCatRequest = record
end;
TCatResponse = record
end;
TDogRequest = record
end;
TDogResponse = record
end;
TMultiAnimalHandler=class(TInterfacedObject,
IRequestHandler<TCatResponse, TCatRequest>,
IRequestHandler<TDogResponse, TDogRequest>)
public
function IRequestHandler<TCatResponse, TCatRequest>.Handle = HandleCatRequest;
function HandleCatRequest(ARequest: TCatRequest): TCatResponse;
function IRequestHandler<TDogResponse, TDogRequest>.Handle = HandleDogRequest;
function HandleDogRequest(ARequest: TDogRequest): TDogResponse;
end;
问题是,当我尝试对特定接口 IRequestHandler<TCatResponse, TCatRequest>
和 IRequestHandler<TDogResponse, TDogRequest>
进行类型转换时,它并不总是解析为正确的方法。
完整示例:
type
IRequestHandler<TResponse: record; TRequest: record> = Interface(IInvokable)
['{AFF8703B-F2AC-44D6-B82C-43E3492ADBB3}']
function Handle(ARequest: TRequest): TResponse;
End;
TCatRequest = record
end;
TCatResponse = record
end;
TDogRequest = record
end;
TDogResponse = record
end;
TMultiAnimalHandler=class(TInterfacedObject,
IRequestHandler<TCatResponse, TCatRequest>,
IRequestHandler<TDogResponse, TDogRequest>)
public
function IRequestHandler<TCatResponse, TCatRequest>.Handle = HandleCatRequest;
function HandleCatRequest(ARequest: TCatRequest): TCatResponse;
function IRequestHandler<TDogResponse, TDogRequest>.Handle = HandleDogRequest;
function HandleDogRequest(ARequest: TDogRequest): TDogResponse;
end;
function TMultiAnimalHandler.HandleCatRequest(
ARequest: TCatRequest): TCatResponse;
begin
WriteLn('Miav!');
end;
function TMultiAnimalHandler.HandleDogRequest(
ARequest: TDogRequest): TDogResponse;
begin
WriteLn('Woof!');
end;
var
catHandler: IRequestHandler<TCatResponse, TCatRequest>;
dogHandler: IRequestHandler<TDogResponse, TDogRequest>;
multiAnimalHandler: TMultiAnimalHandler;
dogRequest: TDogRequest;
catRequest: TCatRequest;
begin
try
multiAnimalHandler := TMultiAnimalHandler.Create;
dogHandler := multiAnimalHandler;
catHandler := multiAnimalHandler;
// Works
dogHandler.Handle(dogRequest);
// Works
catHandler.Handle(catRequest);
// Works
(multiAnimalHandler as IRequestHandler<TDogResponse, TDogRequest>).Handle(dogRequest);
// Does not work. The Handle function calls HandleDogRequest, even if I cast multiAnimalHandler to IRequestHandler<TCatResponse, TCatRequest>!?
(multiAnimalHandler as IRequestHandler<TCatResponse, TCatRequest>).Handle(catRequest);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
令人不安的是:
(multiAnimalHandler as IRequestHandler<TCatResponse, TCatRequest>).Handle(catRequest);
该代码将调用 HandleDogRequest
而不是我预期的 HandleCatRequest
。
以上代码将产生以下输出:
Woof!
Miav!
Woof!
Woof!
我做错了什么或遗漏了什么?
谢谢!
编辑:解决方法
在我的特定用例中,我可以将我的对象强制转换为具体实现。没有那么漂亮的代码,但它有效。
IRequestHandler<TCatResponse, TCatRequest>(invokable).Handle(catRequest)
您的通用接口分配了一个 guid,因此通用接口的所有具体实现都将共享相同的 guid,这在使用 as
运算符对接口进行类型转换时不是一件好事。查找仅适用于找到的 guid 的第一个实例。
每个不同的界面都需要一个唯一的 GUID。这就是您的代码无法正常工作的原因。这是一个可以追溯到十年前的长期问题:
还相关:
Delphi - how to use Supports with a generic interface GUID?
你将不得不介绍一些助手,例如:
type
IRequestHandler<TResponse: record; TRequest: record> = Interface(IInvokable)
function Handle(ARequest: TRequest): TResponse;
end;
TCatRequest = record
end;
TCatResponse = record
end;
ICatRequestHandler = interface(IRequestHandler<TCatResponse, TCatRequest>)
['{5D2B15AC-B11D-49B4-8249-65D42596CEA9}']
end;
TDogRequest = record
end;
TDogResponse = record
end;
IDogRequestHandler = interface(IRequestHandler<TDogResponse, TDogRequest>)
['{9637C82D-97D3-4F82-B8B2-89CE22092438}']
end;
TMultiAnimalHandler = class(TInterfacedObject, ICatRequestHandler, IDogRequestHandler)
public
function ICatRequestHandler.Handle = HandleCatRequest;
function HandleCatRequest(ARequest: TCatRequest): TCatResponse;
function IDogRequestHandler.Handle = HandleDogRequest;
function HandleDogRequest(ARequest: TDogRequest): TDogResponse;
end;
...
var
catHandler: ICatRequestHandler;
dogHandler: IDogRequestHandler;
multiAnimalHandler: TMultiAnimalHandler;
dogRequest: TDogRequest;
catRequest: TCatRequest;
begin
try
multiAnimalHandler := TMultiAnimalHandler.Create;
dogHandler := multiAnimalHandler;
catHandler := multiAnimalHandler;
dogHandler.Handle(dogRequest);
catHandler.Handle(catRequest);
(multiAnimalHandler as IDogRequestHandler).Handle(dogRequest);
(multiAnimalHandler as ICatRequestHandler).Handle(catRequest);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
我有一个实现通用接口多个版本的对象。为避免混淆,我在 class 中使用了方法解析子句: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Implementing_Interfaces
假设我有一个多动物处理程序,可以处理猫和狗(无论那是什么意思)。处理程序为猫和狗实现处理程序 (IRequestHandler<TResponse, TRequest>
) 接口:
IRequestHandler<TResponse: record; TRequest: record> = Interface(IInvokable)
['{AFF8703B-F2AC-44D6-B82C-43E3492ADBB3}']
function Handle(ARequest: TRequest): TResponse;
End;
TCatRequest = record
end;
TCatResponse = record
end;
TDogRequest = record
end;
TDogResponse = record
end;
TMultiAnimalHandler=class(TInterfacedObject,
IRequestHandler<TCatResponse, TCatRequest>,
IRequestHandler<TDogResponse, TDogRequest>)
public
function IRequestHandler<TCatResponse, TCatRequest>.Handle = HandleCatRequest;
function HandleCatRequest(ARequest: TCatRequest): TCatResponse;
function IRequestHandler<TDogResponse, TDogRequest>.Handle = HandleDogRequest;
function HandleDogRequest(ARequest: TDogRequest): TDogResponse;
end;
问题是,当我尝试对特定接口 IRequestHandler<TCatResponse, TCatRequest>
和 IRequestHandler<TDogResponse, TDogRequest>
进行类型转换时,它并不总是解析为正确的方法。
完整示例:
type
IRequestHandler<TResponse: record; TRequest: record> = Interface(IInvokable)
['{AFF8703B-F2AC-44D6-B82C-43E3492ADBB3}']
function Handle(ARequest: TRequest): TResponse;
End;
TCatRequest = record
end;
TCatResponse = record
end;
TDogRequest = record
end;
TDogResponse = record
end;
TMultiAnimalHandler=class(TInterfacedObject,
IRequestHandler<TCatResponse, TCatRequest>,
IRequestHandler<TDogResponse, TDogRequest>)
public
function IRequestHandler<TCatResponse, TCatRequest>.Handle = HandleCatRequest;
function HandleCatRequest(ARequest: TCatRequest): TCatResponse;
function IRequestHandler<TDogResponse, TDogRequest>.Handle = HandleDogRequest;
function HandleDogRequest(ARequest: TDogRequest): TDogResponse;
end;
function TMultiAnimalHandler.HandleCatRequest(
ARequest: TCatRequest): TCatResponse;
begin
WriteLn('Miav!');
end;
function TMultiAnimalHandler.HandleDogRequest(
ARequest: TDogRequest): TDogResponse;
begin
WriteLn('Woof!');
end;
var
catHandler: IRequestHandler<TCatResponse, TCatRequest>;
dogHandler: IRequestHandler<TDogResponse, TDogRequest>;
multiAnimalHandler: TMultiAnimalHandler;
dogRequest: TDogRequest;
catRequest: TCatRequest;
begin
try
multiAnimalHandler := TMultiAnimalHandler.Create;
dogHandler := multiAnimalHandler;
catHandler := multiAnimalHandler;
// Works
dogHandler.Handle(dogRequest);
// Works
catHandler.Handle(catRequest);
// Works
(multiAnimalHandler as IRequestHandler<TDogResponse, TDogRequest>).Handle(dogRequest);
// Does not work. The Handle function calls HandleDogRequest, even if I cast multiAnimalHandler to IRequestHandler<TCatResponse, TCatRequest>!?
(multiAnimalHandler as IRequestHandler<TCatResponse, TCatRequest>).Handle(catRequest);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
令人不安的是:
(multiAnimalHandler as IRequestHandler<TCatResponse, TCatRequest>).Handle(catRequest);
该代码将调用 HandleDogRequest
而不是我预期的 HandleCatRequest
。
以上代码将产生以下输出:
Woof!
Miav!
Woof!
Woof!
我做错了什么或遗漏了什么?
谢谢!
编辑:解决方法
在我的特定用例中,我可以将我的对象强制转换为具体实现。没有那么漂亮的代码,但它有效。
IRequestHandler<TCatResponse, TCatRequest>(invokable).Handle(catRequest)
您的通用接口分配了一个 guid,因此通用接口的所有具体实现都将共享相同的 guid,这在使用 as
运算符对接口进行类型转换时不是一件好事。查找仅适用于找到的 guid 的第一个实例。
每个不同的界面都需要一个唯一的 GUID。这就是您的代码无法正常工作的原因。这是一个可以追溯到十年前的长期问题:
还相关:
Delphi - how to use Supports with a generic interface GUID?
你将不得不介绍一些助手,例如:
type
IRequestHandler<TResponse: record; TRequest: record> = Interface(IInvokable)
function Handle(ARequest: TRequest): TResponse;
end;
TCatRequest = record
end;
TCatResponse = record
end;
ICatRequestHandler = interface(IRequestHandler<TCatResponse, TCatRequest>)
['{5D2B15AC-B11D-49B4-8249-65D42596CEA9}']
end;
TDogRequest = record
end;
TDogResponse = record
end;
IDogRequestHandler = interface(IRequestHandler<TDogResponse, TDogRequest>)
['{9637C82D-97D3-4F82-B8B2-89CE22092438}']
end;
TMultiAnimalHandler = class(TInterfacedObject, ICatRequestHandler, IDogRequestHandler)
public
function ICatRequestHandler.Handle = HandleCatRequest;
function HandleCatRequest(ARequest: TCatRequest): TCatResponse;
function IDogRequestHandler.Handle = HandleDogRequest;
function HandleDogRequest(ARequest: TDogRequest): TDogResponse;
end;
...
var
catHandler: ICatRequestHandler;
dogHandler: IDogRequestHandler;
multiAnimalHandler: TMultiAnimalHandler;
dogRequest: TDogRequest;
catRequest: TCatRequest;
begin
try
multiAnimalHandler := TMultiAnimalHandler.Create;
dogHandler := multiAnimalHandler;
catHandler := multiAnimalHandler;
dogHandler.Handle(dogRequest);
catHandler.Handle(catRequest);
(multiAnimalHandler as IDogRequestHandler).Handle(dogRequest);
(multiAnimalHandler as ICatRequestHandler).Handle(catRequest);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.