我有一个现有的类,它有一个现有的方法,允许你传递一个东西列表:
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> 来自 IEnumerableICollection 来自 IEnumerableICollection<T> 来自 IEnumerableIList 来自 IEnumerableIList<T> 来自 IEnumerableList 来自 IEnumerableList<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.必须有一个编译时机制来迭代列表.但是那是什么原因,那是什么原因.
TObjectList<T>派生自TList<T>,因此请使用它作为参数,如果需要支持列表中的多种对象类型,则使方法本身成为泛型,然后使用for-in 循环枚举列表(这也适用于非泛型TList和各种其他容器类):
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()但可枚举。