IEnumerable<T>
是 Java 的 Iterable<T>
的等价物。由于早期版本的 C# 没有泛型,因此 IEnumerable
是当时唯一可用的迭代器。您可以将其视为一种 IEnumerable<object>
。
大多数泛型集合类型都实现了 IEnumerable<T>
,包括数组。泛型变体要求实现非泛型变体,因此大多数集合(无论是泛型还是非泛型)都实现了 IEnumerable
。
然而,这些迭代器不仅限于表示集合。它们提供了允许您枚举项目的方法,这些方法也可以通过算法生成项目。例如,一个理论上无限的平方数枚举可以通过一个不需要在任何地方存储这些数字的枚举来提供。
在您的情况下,IEnumerable<MaliciousSmall> MaliciousCode
属性可以从数据库中逐个产生 MaliciousSmall
对象,而不需要先将它们存储在集合对象中。
自己实现 IEnumerable<T>
需要实现 IEnumerator<T> GetEnumerator()
方法。它返回一个枚举器对象,需要实现方法 bool MoveNext()
、void Dispose()
、void Reset()
和属性 T Current { get; }
。
你可以通过编写大量代码来实现这些接口,也可以使用 C# 的迭代器。迭代器在幕后使用了许多编译器魔法来自动创建可枚举和枚举器。参见:迭代器 (C# 和 Visual Basic)。
作为C#迭代器的一个示例,让我们使用它们来实现您的示例(我删除了设置器,因为它在这里妨碍)。
public class PagedMalicious : Shared.Paged
{
public IEnumerable<MaliciousSmall> MaliciousCode
{
get
{
using (var conn = new SqlConnection("<my server connection>")) {
var cmd = new SqlCommand("SELECT name, number FROM myTable", conn);
conn.Open();
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) {
var maliciousSmall = new MaliciousSmall {
Name = reader.GetString(0),
Number = reader.GetInt32(1)
};
yield return maliciousSmall;
}
}
}
}
}
}
每次执行
yield return
时,控制权都会传递回调用者,并获取下一个项。getter方法的状态保持不变,并在此处停止执行,直到调用者继续迭代并需要下一个项为止。当他这样做时,执行就会在
yield return
语句之后恢复。
从这个例子中可以看出,枚举以一种惰性的方式进行评估。以下代码总结了整个表格的数字。项目永远不会存储在集合中;它们是从数据库中检索并按照枚举创建的。如果您有一百万条记录,这是一个优势!(在生产代码中,您将使用SQL
SUM
聚合函数。)
var pagedMalicious = new PagedMalicious();
int sum = 0;
foreach (MaliciousSmall item in pagedMalicious.MaliciousCode) {
sum += item.Number;
}
IEnumerable<T>
和 Java 的Iterable<T>
是完全等价的。 - SLaks