【问题标题】:Delphi calls the incorrect function of my generic interface, when calling the function on a reference to the the concrete interface type在对具体接口类型的引用上调用函数时,Delphi 调用了我的通用接口的错误函数
【发布时间】:2022-01-03 10:32:21
【问题描述】:

我有一个实现多个版本的通用接口的对象。为避免混淆,我在课堂上使用方法解析子句: 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&lt;TCatResponse, TCatRequest&gt;IRequestHandler&lt;TDogResponse, TDogRequest&gt; 进行类型转换时,它并不总是解析为正确的方法。

完整示例:

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)

【问题讨论】:

    标签: delphi interface


    【解决方案1】:

    您的泛型接口分配有一个 guid,因此泛型接口的所有具体实现都将共享相同的 guid,这对于使用 as 运算符的类型转换接口来说不是一件好事。查找仅适用于找到的第一个 guid 实例。

    每个不同的界面都需要一个唯一的 guid。这就是您的代码无法正常工作的原因。这是一个可以追溯到十多年前的长期问题:

    Generic interfaces and 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.
    

    【讨论】:

    • 谢谢雷米。这很好地解释了它。我想我需要修改我的调解员。
    • 对于任何阅读本文的人,我已经为我的答案添加了解决方法
    猜你喜欢
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    • 2015-05-26
    • 2011-01-17
    • 2011-05-24
    • 1970-01-01
    • 2022-11-04
    • 1970-01-01
    相关资源
    最近更新 更多