【问题标题】:Test if an interface equals a type parameter测试接口是否等于类型参数
【发布时间】:2017-04-28 12:01:08
【问题描述】:

我有一个可以保存接口的列表。
我想要一个删除功能,它只删除支持特定接口的项目。

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 没有可以使用这些参数调用的“支持”的重载版本

有没有办法做到这一点?

【问题讨论】:

  • 您是否为界面生成了 GUID?编译器通过其 GUID 识别接口。
  • 您声明要删除实现特定接口的项目。如果是这样,那么列表应该包含对象,而不是接口。 Supports 方法旨在像这样使用:Supports(myObject, IMyInterface, Intf),其中 Intf 是 IMyInterface 类型的变量,如果 myObject 实现它,则将其分配给 Intf。 (这是特定重载版本 Supports 的示例,我认为您应该使用该版本)
  • @David Heffernan,是的,您可以将通用参数约束为接口。问题是,它只适用于通用扩展。类型信息不会进入编译阶段。也就是说,即使“I”被限制为 IBar,编译器在调用“Supports”时并不知道它应该是一个接口
  • @DaveOlson 谢谢

标签: delphi generics interface


【解决方案1】:

是的,虽然您不能像这样传递接口类型参数,但您可以传递 TGUID 并将接口类型分配给 TGUID,前提是您在接口声明开始时声明了 GUID。

例子

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;

【讨论】:

  • 不需要传入GUID;它可以安全地从类型信息中提取,因为泛型参数被限制为(至少)类型 IFoo 的接口。它可以通过对“支持”的调用进行一次更改来完成。看我的回答。
【解决方案2】:

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

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

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

然后,您应该能够将对“支持”的调用更改为:

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;

虽然编译器不“知道”“I”是一种接口类型,但您知道 - 因为您对它进行了约束。因此,从“I”的 TypeData 中提取 GUID 是安全的。


泛型类型约束(类、接口或记录)在引用或使用的代码中声明或细化泛型参数的数据类型。相反,它们只是允许编译器限制泛型类型的使用方式。

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;

约束将允许这些:

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

但不是这个:

     L : TMyList<IBar>;

而且,最重要的是,在 3 个有效扩展中,“T”不会成为接口类型。在编译器看来,它仍然是通用的或“类型未知”。

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

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;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多