测试接口是否等于类型参数 [英] Test if an interface equals a type parameter

查看:122
本文介绍了测试接口是否等于类型参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可以存放接口的列表。

我想要一个删除函数,它只删除支持特定接口的项目。

 类型
TMyList< T:IFoo> = class
procedure删除(const Item:T);超载;
程序删除< I:IBar>(const Item:T);超载;
end;

procedure TMyList< T> .Remove< I>(const Item:T);
开始
支持(Item,I)然后Remove(Item);
end;




E2250没有可以调用的'Supports'的重载版本与这些论点


有没有办法可以做到这一点?

解决方案

首先,这两个接口应该有一个GUID声明(如果你还没有这样做)。

  type 
IBar = interface
['{08C764E8-3EF4-42A7-94B7-8D1C5371DF25}']
end;

IFoo =接口
['{30B9440D-A012-4C7D-8422-A80D7E18F4E5}']
end;

然后,您应该可以将呼叫更改为支持:

 使用System.TypInfo; 

函数TMyList< T> .Remove< I>(const Item:T);

begin
// if支持(Item,I)然后Remove(Item);
支持(Item,GetTypeData(TypeInfo(I))^。GUID)然后Remove(Item);

// ...
end;

虽然编译器不知道I是一种接口类型,但是您可以这样做 - 因为你制约了它。因此,从TypeData中为I提取GUID是安全的。


$ b

泛型类型约束(类,或接口或记录) 在引用或使用的代码中声明或优化泛型参数的数据类型。

 类型
IBar =接口
['{08C764E8-3EF4-42A7-94B7-8D1C5371DF25}']
end;

IFoo =接口
['{30B9440D-A012-4C7D-8422-A80D7E18F4E5}']
end;

IFoo1 =接口(IFoo)
['{6C3D06C9-C0B7-45CD-80F2-45123ECC5E9C}']
end;
IFoo2 =接口(IFoo)
['{9C3AB5F7-E88C-4620-AFC5-B83340048531}']
end;


TMyList< T:IFoo> = class
end;

< T:IFoo>约束将允许这些:

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

但这不是:

  L:TMyList< IBar> ;; 

最重要的是,在3个有效的扩展中,T成为一种界面类型。它在编译器的眼中仍然是泛型的或类型未知的。



构造函数约束是这个规则的一个例外。它需要泛型参数为类类型,但更重要的是,它允许通过无参数构造函数实例化目标类,而无需类型转换。

 类型
TMyClass = class
构造函数Create;
程序DoSomething;
end;

构造函数TMyClass.Create;
begin
end;

过程TMyClass.DoSomething;
begin
end;

类型
TMyGeneric2< T:构造函数> = class
过程执行;
end;

procedure TMyGeneric2< T> ;.Perform;

var
x:T;

begin
x:= T.Create; //这是允许的...
x.DoSomething; // ...但这不是。
end;

当您使用泛型类型参数约束时,不解析类型的通用参数。您只需设置一些规则,供编译器在使用该泛型类型时遵循。


I have a list that can hold interfaces.
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;

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

Is there a way in which this can be done?

解决方案

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".


Generic type constraints (either class, or interface or record) do not 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" does not 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 class type but more importantly, it allows instantiation of the target class through a parameter-less constructor without 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, you are not resolving the type of the generic parameter. You are merely setting-up some rules for the compiler to follow when that generic type is consumed.

这篇关于测试接口是否等于类型参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆