IEnumerator的简单用法(带有示例)


73

我很难记住如何(但不是为什么)IEnumerator在C#中使用s。我已经习惯了Java及其出色的文档,该文档向初学者很好地解释了所有内容。所以,请忍受我。

我曾尝试从这些板上的其他答案中学习,但无济于事。我没有提出一个之前已经问过的通用问题,而是有一个具体的例子可以为我澄清一些事情。

假设我有一个需要传递一个IEnumerable<String>对象的方法。方法所需要做的就是将字母连接roxxorsString迭代器中每个字母的末尾。然后它将返回这个新的迭代器(当然,原始IEnumerable对象将保留原样)。

我将如何处理?当然,除了我之外,这里的答案还应该帮助许多有关这些对象的基本问题。

Answers:


104

这是有关的文档IEnumerator。它们用于获取列表的值,其中长度不一定要提前知道(即使可以知道)。这个词来自enumerate,意思是“数一数二或一一列举”。

IEnumerator并且IEnumerator<T>由所有提供IEnumerableIEnumerable<T>接口经由(后者提供两者)在.NET GetEnumerator()。这很重要,因为该foreach语句旨在通过这些接口方法直接与枚举器一起使用。

因此,例如:

IEnumerator enumerator = enumerable.GetEnumerator();

while (enumerator.MoveNext())
{
    object item = enumerator.Current;
    // Perform logic on the item
}

成为:

foreach(object item in enumerable)
{
    // Perform logic on the item
}

对于您的特定情况,.NET中几乎所有集合都实现IEnumerable。因此,您可以执行以下操作:

public IEnumerator Enumerate(IEnumerable enumerable)
{
    // List implements IEnumerable, but could be any collection.
    List<string> list = new List<string>(); 

    foreach(string value in enumerable)
    {
        list.Add(value + "roxxors");
    }
    return list.GetEnumerator();
}

foreach不会调用Reset()。它用于COM互操作,并且并非所有枚举器都支持它(实际上大多数枚举器都不支持它)。
R. Martinho Fernandes

枚举器是只读的,对吗?如何使用这种模式返回包含我的修改的枚举器类型?
BlackVegetable 2011年

8
@BlackVegetableIEnumerator是只读的,但是IEnumerable通常可以将其强制转换回其可变类型(例如((List<string>)foo.ReadOnlyValues).Add("Hahaha! So much for read only sucker!"))-因此,在假设这样做IEnumerable可以保护您免受此攻击时,请当心。
乔纳森·迪金森

@JonathanDickinson Eh ...实际上,这消除了使用IEnumerable的所有好处。典型的模式是返回带有新值的新IEnumerable。这使客户端代码可以决定何时评估可枚举的每个元素,而不是强制所有这些元素立即全部发生。
jpaugh

18
public IEnumerable<string> Appender(IEnumerable<string> strings)
{
  List<string> myList = new List<string>();
  foreach(string str in strings)
  {
      myList.Add(str + "roxxors");
  }
  return myList;
}

要么

public IEnumerable<string> Appender(IEnumerable<string> strings)
{
  foreach(string str in strings)
  {
      yield return str + "roxxors";
  }
}

使用yield构造,或简单地

var newCollection = strings.Select(str => str + "roxxors"); //(*)

要么

var newCollection = from str in strings select str + "roxxors"; //(**)

后面的两个使用LINQ(**)只是语法糖(*)


LINQ有趣的模式。我必须对此进行研究。谢谢大家的(快速)帮助!
BlackVegetable 2011年

我似乎记得,您应该尽量不要在声明为IEnumerable的方法中返回List。原因是人们可能会“作弊”并注意到它实际上是一个列表,并开始使用它进行列表操作。List(实际上是所有IEnumerables)的确有一个AsEnumerable()方法,我认为应该调用该方法并返回它。否则,将是产量的最佳答案,尤其是linq响应。:)
克里斯

如果您打算用C#进行大量开发,则您肯定会在LINQ上花费一些时间-这样可以节省很多时间,并使您对代码的感觉更好:-)
Rune

@chris:调用.AsEnumerable()并没有做很多事情:stackoverflow.com/questions/17968469/… :-)
符文

1
@rune:过去有点爆炸。:)是正确的,IEnumerables上的AsEnumerable方法没有任何用处。我的推理仍然很合理,但需要使用其他方法才能停止将其投射为列表(例如.Selectg(x=>x))。
克里斯

8

如果我正确理解了您的意思,那么在C#中,yield return我认为是您所需要的全部。

例如

IEnumerable<string> myMethod(IEnumerable<string> sequence)
{
    foreach(string item in sequence)
    {
         yield return item + "roxxors";
    }
}

因此,在这种情况下,yield每次运行时都会从列表中返回单个(修改后的)字符串吗?还是会返回某种IEnumerable对象?
BlackVegetable 2011年

@BlackVegetable是的,它将返回一个IEnumerable对象,该对象每次MoveNext被调用时都将构建一个字符串(直接或间接与foreach一起使用)
R. Martinho Fernandes

是的,它是语法糖,它告诉编译器将其转换为迭代器块。请参阅此处msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
Ben Robinson

这是正确的方法,应该提高。接受的答案必须在返回之前构建整个列表,而不是遵循IEnumerable设计。
Thibault D.

5

我会做类似的事情:

private IEnumerable<string> DoWork(IEnumerable<string> data)
{
    List<string> newData = new List<string>();
    foreach(string item in data)
    {
        newData.Add(item + "roxxors");
    }
    return newData;
}

简单的东西:)


这是我的第一个想法。但是,我与之交互的其他方法要求我返回IEnumerable类型或诸如此类。谢谢您的回应!
BlackVegetable 2011年

@BlackVegetable:List<T>是一个IEnumerable<T>。但是,是的,有更好的方法(使用yield)。
R. Martinho Fernandes

2
我似乎记得,您应该尽量不要在声明为IEnumerable的方法中返回List。原因是人们可能会“作弊”并注意到它实际上是一个列表,并开始使用它进行列表操作。List(实际上是所有IEnumerables)的确有一个AsEnumerable()方法,我认为应该调用该方法并返回它。
克里斯(Chris

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.