有人可以提供关于迭代器使用的真实例子。我试着搜索谷歌,但对答案不满意。
答案 0 :(得分:15)
您可能听说过数组和容器 - 存储其他对象列表的对象。
但是为了使对象表示列表,它实际上不必“存储”列表。它所要做的就是为您提供允许您获取列表项的方法或属性。
在.NET框架中,接口IEnumerable是一个对象必须支持在这个意义上被视为“列表”。
为了简化一点(省略一些历史包袱):
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
}
所以你可以从中获得一个枚举器。该界面(再次,略微简化以消除分散注意力的噪音):
public interface IEnumerator<T>
{
bool MoveNext();
T Current { get; }
}
因此,要遍历列表,您需要执行此操作:
var e = list.GetEnumerator();
while (e.MoveNext())
{
var item = e.Current;
// blah
}
foreach
关键字:
foreach (var item in list)
// blah
但是创建一种新的列表呢?是的,我们可以使用List<T>
并填写项目。但是,如果我们想要在需要时“动态”发现这些物品呢?这样做有一个好处,即客户端可以在前三个项目之后放弃迭代,并且他们不必“支付生成整个列表的成本”。
手动实现这种懒惰列表会很麻烦。我们必须编写两个类,一个用于通过实现IEnumerable<T>
来表示列表,另一个用于通过实现IEnumerator<T>
来表示活动的枚举操作。
迭代器方法为我们做了所有艰苦的工作。我们只写:
IEnumerable<int> GetNumbers(int stop)
{
for (int n = 0; n < stop; n++)
yield return n;
}
编译器将此转换为两个类。调用该方法等同于构造表示该列表的类的对象。
答案 1 :(得分:12)
迭代器是一种抽象,它将集合中的位置概念与集合本身分离。迭代器是一个单独的对象,存储必要的状态以定位集合中的项目并移动到集合中的下一个项目。我已经看到集合在集合中保持该状态(即当前位置),但通常最好将该状态移动到外部对象。除此之外,它还允许您使用多个迭代器迭代相同的集合。
答案 2 :(得分:5)
简单示例:生成整数序列的函数:
static IEnumerable<int> GetSequence(int fromValue, int toValue)
{
if (toValue >= fromValue)
{
for (int i = fromValue; i <= toValue; i++)
{
yield return i;
}
}
else
{
for (int i = fromValue; i >= toValue; i--)
{
yield return i;
}
}
}
要在没有迭代器的情况下执行此操作,您需要创建一个数组,然后枚举它...
答案 3 :(得分:4)
通过课堂上的学生进行迭代
Iterator设计模式提供 我们用一种常用的枚举方法 隐藏时的项目或数组列表 列表的详细信息 实现。这提供了一个 更清洁地使用数组对象和 从中隐藏不必要的信息 客户,最终导致 更好的代码重用,增强 可维护性和更少的错误。该 迭代器模式可以枚举 物品清单,无论他们的 实际存储类型。
答案 4 :(得分:3)
答案 5 :(得分:2)
他们非常喜欢的几件事:
a)在保持代码整洁的同时“感知性能” - 与其他处理逻辑分离的东西的迭代
b)当您要迭代的项目数量未知时。
虽然两者都可以通过其他方式完成,但是使用迭代器可以使代码变得更好更整洁,因为调用迭代器的人不需要担心它是如何找到迭代的东西...
真实生活示例:枚举目录和文件,并找到满足某些条件的第一个[n],例如:包含特定字符串或序列等的文件......
答案 6 :(得分:2)
除了其他一切,迭代懒惰型序列 - IEnumerators。可以在迭代步骤中评估/初始化这种序列的每个下一个元素,这使得可以使用有限量的资源迭代无限序列......
答案 7 :(得分:2)
规范和最简单的例子是,它使无限序列成为可能,而不必自己编写类来完成这一过程的复杂性:
// generate every prime number
public IEnumerator<int> GetPrimeEnumerator()
{
yield return 2;
var primes = new List<int>();
primesSoFar.Add(2);
Func<int, bool> IsPrime = n => primes.TakeWhile(
p => p <= (int)Math.Sqrt(n)).FirstOrDefault(p => n % p == 0) == 0;
for (int i = 3; true; i += 2)
{
if (IsPrime(i))
{
yield return i;
primes.Add(i);
}
}
}
显然,除非你使用BigInt而不是int,否则这不会真正无限,但它会给你一个想法。 为每个生成的序列编写此代码(或类似代码)将是乏味且容易出错的。迭代器为你做这件事。如果上面的例子看起来太复杂,你可以考虑:
// generate every power of a number from start^0 to start^n
public IEnumerator<int> GetPowersEnumerator(int start)
{
yield return 1; // anything ^0 is 1
var x = start;
while(true)
{
yield return x;
x *= start;
}
}
他们付出了代价。它们的惰性行为意味着您不能发现常见错误(空参数等),直到生成器第一次消耗而不是创建而不先编写包装函数进行检查。如果递归使用,当前的实现也难以置信坏(1)。
在树和对象图等复杂结构上的枚举更容易编写,因为状态维护主要是为你完成的,你必须简单地编写代码来访问每个项目而不用担心回来它。
答案 8 :(得分:1)
迭代器是实现IEnumerator接口的简单方法。您只需创建一个逐个返回值的方法,而不是创建具有接口所需的方法和属性的类,编译器将创建一个具有实现接口所需的方法和属性的类。
例如,如果您有一个庞大的数字列表,并且想要返回一个集合,其中每个数字乘以2,您可以创建一个返回数字的迭代器,而不是在内存中创建列表的副本: / p>
public IEnumerable<int> GetDouble() {
foreach (int n in originalList) yield return n * 2;
}
在C#3中,你可以使用扩展方法和lambda表达式做类似的事情:
originalList.Select(n => n * 2)
或使用LINQ:
from n in originalList select n * 2
答案 9 :(得分:1)
IEnumerator<Question> myIterator = listOfStackOverFlowQuestions.GetEnumerator();
while (myIterator.MoveNext())
{
Question q;
q = myIterator.Current;
if (q.Pertinent == true)
PublishQuestion(q);
else
SendMessage(q.Author.EmailAddress, "Your question has been rejected");
}
foreach (Question q in listOfStackOverFlowQuestions)
{
if (q.Pertinent == true)
PublishQuestion(q);
else
SendMessage(q.Author.EmailAddress, "Your question has been rejected");
}