传递清单的目的是什么?

Ian*_*oyd 7 delphi delphi-xe6

我有一个现有的类,它有一个现有的方法,允许你传递一个东西列表:

TContoso = class(TSkyrim)
public
   procedure AddObjects(Objects: TList);
end;
Run Code Online (Sandbox Code Playgroud)

所以,在之前的时代,有人可以传递一个TList或一个TObjectList方法:

var
   list: TList;

list := TObjectList.Create(True);
contoso.AddObjects(list);
Run Code Online (Sandbox Code Playgroud)

没关系,就像TObjectList一个TList.我的方法很灵活; 它可能需要.

现在在以后的时代

现在在以后,我更喜欢打字列表:

var
   list: TList<TGrobber>;

list := TObjectList<TGrobber>.Create(True);
contoso.AddObjects(list);
Run Code Online (Sandbox Code Playgroud)

当然,这并不编译,既不的TList<T>,也不TObjectList<T>从下降TList.哪个不是这个问题.我直觉地明白,我实际上并不需要TList,我只需要"可枚举"的东西:

根据我在.NET FCL中的经验,这意味着我只需要声明参数是IEnumerable,因为一切都是可枚举的:

  • IEnumerable<T> 来自 IEnumerable
  • ICollection 来自 IEnumerable
  • ICollection<T> 来自 IEnumerable
  • IList 来自 IEnumerable
  • IList<T> 来自 IEnumerable
  • List 来自 IEnumerable
  • List<T> 来自 IEnumerable

所以我会这样做:

TContoso = class(TSkyrim)
public
   procedure AddObjects(Objects: IEnumerable);
end;    
Run Code Online (Sandbox Code Playgroud)

除了Delphi,BCL不允许.NET世界允许的多态性; 可枚举的东西不实现IEnumerable接口:

TList = class(TObject)
public
   function GetEnumerator: TListEnumerator;
end;

TObjectList = class(TList);

TList<T> = class(TEnumerable<T>)
public
   function GetEnumerator: TEnumerator<T>;
end;

TObjectList<T> = class(TList<T>);
Run Code Online (Sandbox Code Playgroud)

没有输入,编译器如何知道类型是可枚举的?

德尔福使用秘密的硬编码魔法.:

类或接口必须实现规定的集合模式.实现集合模式的类型必须具有以下属性: - 类或接口必须包含名为的公共实例方法GetEnumerator().该GetEnumerator()方法必须返回类,接口或记录类型.返回的类,接口或记录GetEnumerator()必须包含一个名为的公共实例方法MoveNext().该MoveNext()方法必须返回一个布尔值.- 返回的类,接口或记录GetEnumerator()必须包含一个公共实例,名为read-only属性Current.该类型的Current属性必须是集合中包含的类型.

语言设计者打算在我的代码中使用可枚举的方式是什么?

  • 我怎么声明参数的类型
  • 我如何检查是否存在一个名为GetEnumerator?的方法?
  • 我怎么称呼这个方法GetEnumerator?
  • 我怎么称呼Current房产?
  • 我怎么称呼这个Next方法?

例如:

TContoso = class(TSkyrim)
public
   procedure AddObjects(const Objects);
end;    

procedure TContoso.AddObjects(const Objects);
var
   o: TObject;
   enumerator: TObject;
   bRes: Boolean;
begin
   //for o in Objects do
   //   InternalAdd(nil, '', o);

   if not HasMethod(Objects, 'GetEnumerator') then
      Exit;

    enumerator := InvokeMethod(Objects, 'GetEnumerator');

    if not HasMethod(enumerator, 'MoveNext') then
       Exit;

    bRes := InvokeMethod(enumerator, 'MoveNext');

    while bRes do
    begin
       if HasMethod(enumerator, 'Current');
           InternallAdd(nil, '', InvokeMethod(enumerator, 'Current'));

       bRes := InvokeMethod(enumerator, 'MoveNext');
    end;
end;
Run Code Online (Sandbox Code Playgroud)

传递"一大堆东西"的目的是什么?

劈

TContoso = class(TSkyrim)
public
   procedure AddObjects(Objects: TList); overload;
   procedure AddObjects(Objects: TList<T>); overload;
end;    
Run Code Online (Sandbox Code Playgroud)

设计师必须选择不IList实施IEnumerable.必须有一个编译时机制来迭代列表.但是那是什么原因,那是什么原因.

Rem*_*eau 1

TObjectList<T>派生自TList<T>,因此请使用它作为参数,如果需要支持列表中的多种对象类型,则使方法本身成为泛型,然后使用for-in 循环枚举列表(这也适用于非泛型TList和各种其他容器类):

使用 For 语句迭代容器

Type
  TContoso = class(TSkyrim)
  public
    procedure AddObjects(Objects: TList); overload;
    procedure AddObjects<T: class>(Objects: TList<T>); overload;
  end;

procedure TContoso.AddObjects(Objects: TList);
var
  Obj: Pointer;
begin
  for Obj in Objects do
  begin
    // use TObject(Obj) as needed...
  end;
end;

procedure TContoso.AddObjects<T>(Objects: TList<T>);
var
  Obj: T;
begin
  for Obj in Objects do
  begin
    // use Obj as needed...
  end;
end;
Run Code Online (Sandbox Code Playgroud)

var
   list: TList;

list := TObjectList.Create(True);
contoso.AddObjects(list);
Run Code Online (Sandbox Code Playgroud)

var
  list: TList<TGrobber>;

list := TObjectList<TGrobber>.Create(True);
contoso.AddObjects<TGrobber>(list);
Run Code Online (Sandbox Code Playgroud)

让编译器验证返回的枚举器类的存在GetEnumerator()及其子方法。不要尝试手动处理它(如果你想这样做,你必须使用 RTTI)。此外,for-in循环还内置支持其他类型的容器(数组、字符串、集合和记录),这些容器不公开GetEnumerator()但可枚举。