Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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. To avoid confusion, I use method resolution clauses in my 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: 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.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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 HandleDogRequestand not HandleCatRequest, which I expected.

The above code will produce the following output:

Woof!
Miav!
Woof!
Woof!

What am I doing wrong or missing?

Thanks!

>Solution :

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. The lookup will only work for the first instance of the guid found.

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

Also related:

Delphi – how to use Supports with a generic interface GUID?

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading