Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When a class implements a descendant interface, why doesn't it automatically count as implementing the base interface?

What's the reason this won't compile?

type
  IInterfaceA = interface ['{44F93616-0161-4912-9D63-3E8AA140CA0D}']
    procedure DoA;
  end;

  IInterfaceB = interface(IInterfaceA) ['{80CB6D35-E12F-462A-AAA9-E7C0F6FE0982}']
    procedure DoB;
  end;

  TImplementsAB = class(TSingletonImplementation, IInterfaceB)
    procedure DoA;
    procedure DoB;
  end;

var
  ImplementsAB: TImplementsAB;
  InterfaceA: IInterfaceA;
  InterfaceB: IInterfaceB;
begin
  ImplementsAB := TImplementsAB.Create;
  InterfaceA := ImplementsAB; >> incompatible types
  ...
end

In contrast this is how I make it work:

InterfaceA := ImplementsAB as InterfaceB;

or

InterfaceA := InterfaceB;

I mean, if IInterfaceB inherits from IInterfaceA and TImplementsAB implements IInterfaceB, it wouldn't be logical to also implement IInterfaceA and be type compatible?

like image 428
pragmatic_programmer Avatar asked Dec 11 '11 22:12

pragmatic_programmer


2 Answers

This so because early OLE/COM had a bug and Borland decided to be compatible with it. This is mentioned in this article: New Delphi language feature: Multiple inheritance for interfaces in Delphi for .NET. The solution is to list all ancestor interfaces explicitly in the class as Mikael wrote.

Some quotes from the linked article:

The problem was in COM itself. To load a module, COM would load the DLL, GetProcAddress on a well-known entry point that was supposed to be exported from the DLL, call the DLL function to obtain an IUnknown interface, and then QueryInterface for IClassFactory. The problem was, when Microsoft added support for IClassFactory2, they added the QueryInterface for IClassFactory2 after the existing code that queried for IClassFactory. IClassFactory2 would only be requested if the query for IClassFactory failed.

Thus, COM would never request IClassFactory2 on any COM server that implemented both IClassFactory2 and IClassFactory.

This bug existed in COM for a long time. Microsoft said that they couldn't fix the COM loader with an OS service pack because both Word and Excel (at the time) relied on the buggy behavior. Regardless of whether it's fixed in the latest releases of COM or not, Borland has to provide some way to preserve this behavior in Win32 Delphi for the forseeable future. Suddenly adding all ancestors into an implementing class that weren't there before is very likely to break existing code that unintentionally falls into the same pattern as the COM loader.

like image 183
ain Avatar answered Nov 08 '22 13:11

ain


Another way to make it work is to include both interfaces in the class declaration.

TImplementsAB = class(TSingletonImplementation, IInterfaceA, IInterfaceB)
  procedure DoA;
  procedure DoB;
end;

I guess this is what is required for the compiler to realize that TImplementsAB implements both IInterfaceA and IInterfaceB.

like image 38
Mikael Eriksson Avatar answered Nov 08 '22 12:11

Mikael Eriksson