我有一个现有的类,它有一个现有的方法,允许你传递一个的东西列表:
TContoso = class(TSkyrim)
public
procedure AddObjects(Objects: TList);
end;
所以,在之前的时间里,有人可以将TList
或TObjectList
传递给该方法:
var
list: TList;
list := TObjectList.Create(True);
contoso.AddObjects(list);
没关系,因为TObjectList
是TList
。我的方法很灵活;它可能需要。
现在在以后的时间里,我更喜欢打字列表:
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
。必须有一个编译时机制来迭代列表。但是那是什么原因,那是什么原因。
答案 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)”