简体   繁体   English

Delphi 在对具体接口类型的引用上调用 function 时调用了我的通用接口的错误 function

[英]Delphi calls the incorrect function of my generic interface, when calling the function on a reference to the the concrete interface type

I have an object that implements multiple versions of a generic interface.我有一个实现多个版本的通用接口的 object。 To avoid confusion, I use method resolution clauses in my class: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Implementing_Interfaces为避免混淆,我在 class 中使用方法解析子句: https://docwiki.embarcadero.com/RADStudio/Sydney/en/Implementing_Interfaces

Say I have a multi animal handler, that handles both cats and dogs (whatever that means).假设我有一个多动物处理程序,可以处理猫和狗(无论这意味着什么)。 The handler implements a handler ( IRequestHandler<TResponse, TRequest> ) interface for both the cats and dogs:处理程序为猫和狗实现了处理程序( 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;

Problem is, when I try to typecast to the specific interfaces IRequestHandler<TCatResponse, TCatRequest> and IRequestHandler<TDogResponse, TDogRequest> , it doesn't always resolve to the correct method.问题是,当我尝试对特定接口IRequestHandler<TCatResponse, TCatRequest>IRequestHandler<TDogResponse, TDogRequest>进行类型转换时,它并不总是解析为正确的方法。

Full Example:完整示例:

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.

The troubling line is this:令人不安的是:

(multiAnimalHandler as IRequestHandler<TCatResponse, TCatRequest>).Handle(catRequest);

The code will call HandleDogRequest and not HandleCatRequest , which I expected.该代码将调用HandleDogRequest而不是HandleCatRequest ,这是我所期望的。

The above code will produce the following output:上面的代码将产生以下 output:

Woof!
Miav!
Woof!
Woof!

What am I doing wrong or missing?我做错了什么或错过了什么?

Thanks!谢谢!

Edit: Work Around编辑:解决方法

In my specific use case, I could typecast my object to the concrete implementation.在我的特定用例中,我可以将我的 object 类型转换为具体实现。 Not as beautiful code, but it works.没有那么漂亮的代码,但它可以工作。

IRequestHandler<TCatResponse, TCatRequest>(invokable).Handle(catRequest)

Your generic interface has a guid assigned to it, so all concrete implementations of the generic interface will share the same guid, which is not a good thing when it comes to typecasting interfaces using the as operator.您的泛型接口分配了一个 guid,因此泛型接口的所有具体实现都将共享相同的 guid,这对于使用as运算符的类型转换接口不是一件好事。 The lookup will only work for the first instance of the guid found.查找仅适用于找到的 guid 的第一个实例。

Every distinct interface needs a unique guid.每个不同的界面都需要一个独特的指南。 That is why your code doesn't work correctly.这就是您的代码无法正常工作的原因。 This is a long-standing problem going back over a decade:这是一个可以追溯到十多年前的长期问题:

Generic interfaces and GUID 通用接口和 GUID

Also related:还有相关的:

Delphi - how to use Supports with a generic interface GUID? Delphi - 如何使用具有通用接口 GUID 的支持?

You are going to have to introduce some helpers, eg:您将不得不介绍一些助手,例如:

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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM