16767

Test if an interface equals a type parameter

Question:

I have a list that can hold interfaces.<br /> I want to have a remove function that only deletes items that support a particular interface.

type TMyList<T: IFoo> = class procedure Remove(const Item: T); overload; procedure Remove<I: IBar>(const Item: T); overload; end; procedure TMyList<T>.Remove<I>(const Item: T); begin if Supports(Item, I) then Remove(Item); end; <blockquote>

E2250 There is no overloaded version of 'Supports' that can be called with these arguments

</blockquote>

Is there a way in which this can be done?

Answer1:

First, the two interfaces should have a GUID declared with them (If you haven't done this already).

type IBar = interface ['{08C764E8-3EF4-42A7-94B7-8D1C5371DF25}'] end; IFoo = interface ['{30B9440D-A012-4C7D-8422-A80D7E18F4E5}'] end;

Then, you should be able to change the call to "supports" to this:

uses System.TypInfo; function TMyList<T>.Remove<I>(const Item: T); begin //if Supports(Item, I) then Remove(Item); if Supports(Item, GetTypeData( TypeInfo(I) )^.GUID) then Remove(Item); // ... end;

While the compiler does not "know" that "I" is an interface type, you do - because you constrained it. So, it's safe to extract the GUID out of the TypeData for "I".

<hr />

Generic type constraints (either class, or interface or record) <em>do not</em> declare or refine the data type of the generic parameter in the code where that are referenced or consumed. Rather, they simply allow the compiler to restrict how the generic type may be used.

type IBar = interface ['{08C764E8-3EF4-42A7-94B7-8D1C5371DF25}'] end; IFoo = interface ['{30B9440D-A012-4C7D-8422-A80D7E18F4E5}'] end; IFoo1 = interface(IFoo) ['{6C3D06C9-C0B7-45CD-80F2-45123ECC5E9C}'] end; IFoo2 = interface(IFoo) ['{9C3AB5F7-E88C-4620-AFC5-B83340048531}'] end; TMyList<T: IFoo> = class end;

The < T:IFoo > constraint will allow these:

var L : TMyList<IFoo>; L : TMyList<IFoo1>; L : TMyList<IFoo2>;

But not this:

L : TMyList<IBar>;

And, most importantly, in the 3 valid expansions, "T" <strong>does not</strong> become an interface type. It is still generic or "type-unknown" in the compiler's eyes.

The "constructor" constraint is the one exception to this rule. It will require the generic parameter to be a <strong>class type</strong> but more importantly, it allows instantiation of the target class through a parameter-less constructor <em>without</em> type casting.

type TMyClass = class constructor Create; procedure DoSomething; end; constructor TMyClass.Create; begin end; procedure TMyClass.DoSomething; begin end; type TMyGeneric2<T:constructor> = class procedure Perform; end; procedure TMyGeneric2<T>.Perform; var x : T; begin x := T.Create; // This is allowed... x.DoSomething; // ...but this is not. end;

When you use generic type parameter constraints, <strong>you are not resolving the type</strong> of the generic parameter. You are merely setting-up some <strong>rules</strong> for the compiler to follow when that generic type is consumed.

Answer2:

Yes, although you cannot pass an interface type parameter as such, you can pass a TGUID and assign an interface type to a TGUID, provided that you declared a GUID in the interface declaration to begin with.

Example

type IFoo = interface ['{93863A49-5014-4AE5-A7CF-F3F2E044CE57}'] //<Ctrl>+<Shift>+G .... end; IBar = interface(IFoo) ['{88888888-5014-4AE5-A7CF-F3F2E044CE57}'] .... end; procedure TFooList.Remove(const Item: IFoo; const MustBeA: TGuid); {overload;} begin if Supports(Item, MustBeA) then Remove(Item); .... for i:= 0 to count-1 do begin //Only remove IBar16 items MyFooList.Remove(FooList[i], IBar16); end;

Recommend