Delphi - Inheriting Generic Interfaces

I am currently stuck in a compilation error, no one in our company can help, and I, unfortunately, did not find the correct search patterns for SO or google.

As a code, I use 2 interfaces inherited and 2 classes inherited. The following code reproduces the error:

program Project22; {$APPTYPE CONSOLE} type IStorageObject = interface(IInterface) end; TObjectStorage<T: IStorageObject> = class(TObject) end; IKeyStorageObject<TKey> = interface(IStorageObject) end; TKeyObjectStorage<TKey, T: IKeyStorageObject<TKey>> = class(TObjectStorage<T>) end; TImplementingClass<TKey> = class(TInterfacedObject, IKeyStorageObject<TKey>) end; begin TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create; end. 

Compiler error for 'TKeyObjectStorage':

[DCC error] Project22.dpr (11): E2514 A type parameter "T" must support the "IStorageObject" interface

I think that the compiler does not recognize the correctness of the T parameter of the TKeyObjectStorage class. This should be correct, because the required type "IKeyStorageObject" is the parent type IStorageObject.

Why is this not working? What am I doing wrong? Is this not possible in Delphi?

+7
generics inheritance delphi delphi-xe2
source share
1 answer

Update

In the original question, there was a problem that I identified (see below). However, the fix that I am describing is great for XE3 and later, but the program below does not compile in XE2. Thus, I conclude that this is an XE2 generator compiler error.

In any case, the workaround for Delphi XE2 :

 {$APPTYPE CONSOLE} type IStorageObject = interface(IInterface) end; TObjectStorage<T: IStorageObject> = class(TObject) end; IKeyStorageObject<TKey> = interface(IStorageObject) end; TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>, IStorageObject> = class(TObjectStorage<T>) end; TImplementingClass<TKey> = class(TInterfacedObject, IStorageObject, IKeyStorageObject<TKey>) end; begin TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create; end. 

Original answer

It would be better if you provided a complete program in which a compiler error was detected. You need to try to instantiate the object to see this error.

But, I think I reproduced your problem. Therefore, I believe the problem is that this code:

 TKeyObjectStorage<TKey, T: IKeyStorageObject<TKey>> = ... 

applies the general constraint to both TKey and T Now, obviously, you want the constraint to be applied to T , so you need to write:

 TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>> = ... 

Here's a short program that compiles after changing Delphi XE3 :

 {$APPTYPE CONSOLE} type IStorageObject = interface(IInterface) end; TObjectStorage<T: IStorageObject> = class(TObject) end; IKeyStorageObject<TKey> = interface(IStorageObject) end; TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>> = class(TObjectStorage<T>) end; TImplementingClass<TKey> = class(TInterfacedObject, IKeyStorageObject<TKey>) end; begin TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create; end. 

This is a pretty nuance, changing a comma to a semicolon. Programming with significant punctuation is never very fun. However, you are familiar with the difference between commas and half-columns in formal parameter lists, and therefore it should not be too unexpected to see the same difference drawn here.

The documentation covers your opinion:

Parameters of several types

When specifying restrictions, you highlight several parameters such as semicolons, as you do with the declaration of the parameter list:

 type TFoo<T: ISerializable; V: IComparable> 

Like parameter declarations, you can group several type parameters together in a comma list to bind to the same restrictions:

 type TFoo<S, U: ISerializable> ... 

In the above example, S and U bound to an ISerializable constraint.

+9
source share

All Articles