传递清单的目的是什么?

时间:2014-11-02 16:42:22

标签: delphi delphi-xe6

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

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

所以,在之前的时间里,有人可以将TListTObjectList传递给该方法:

var
   list: TList;

list := TObjectList.Create(True);
contoso.AddObjects(list);

没关系,因为TObjectListTList。我的方法很灵活;它可能需要。

现在在次之后

现在在以后的时间里,我更喜欢打字列表:

var
   list: TList<TGrobber>;

list := TObjectList<TGrobber>.Create(True);
contoso.AddObjects(list);

当然,这不会编译,因为TList<T>TObjectList<T>都不会从TList下降。这不是一个问题。我直截了当地明白,我实际上并不需要TList,我只需要一些&#34;可枚举的东西&#34;

根据我在.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;    

除了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>);

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

Delphi uses secret hard-coded magic.

  

类或接口必须实现规定的集合模式。实现集合模式的类型必须具有以下属性:    - 类或接口必须包含名为GetEnumerator()的公共实例方法。 GetEnumerator()方法必须返回类,接口或记录类型。   GetEnumerator()返回的类,接口或记录必须包含名为MoveNext()的公共实例方法。 MoveNext()方法必须返回布尔值。    - GetEnumerator()返回的类,接口或记录必须包含一个名为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;

传递&#34;一大堆东西&#34; 的预期方式是什么?

哈克

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

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

2 个答案:

答案 0 :(得分:1)

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

Iteration Over Containers Using For statements

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;

var
   list: TList;

list := TObjectList.Create(True);
contoso.AddObjects(list);

var
  list: TList<TGrobber>;

list := TObjectList<TGrobber>.Create(True);
contoso.AddObjects<TGrobber>(list);

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

答案 1 :(得分:0)

显然,经典的VMT(虚方法表)结构不支持多重继承,所以他们不希望任何可枚举的东西都要从TEbject下降的TEnumerable下降,因为这样会限制太多。

因此,他们使用了必须在您的类中的特定方法签名的hack,迫使您使用HasMethod / InvokeMethod。所以你的方法确实传递了任何“可枚举”的方法,那就是“过程TContoso.AddObjects(const Objects)”