1

阅读帖子时,没有示例就给出了一些要点:

要实现 IEnumerable / IEnumerable,您必须提供一个枚举器:

• 如果该类正在“包装”另一个集合,则返回被包装的集合的枚举数。

• 通过使用yield return 的迭代器。

•通过实例化您自己的 IEnumerator/IEnumerator 实现

(我的宝贝头脑将其解释为)

(第 1 点)

    If the class is "wrapping" another collection, by returning the
wrapped collection's enumerator.

会不会是..

class  StringCollections
{

 //A class is wrapping another collection
  string[]  names=new string {“Jon Skeet”,”Hamish Smith”,
                  ”Marc Gravell”,”Jrista”,”Joren”};

//by returning the wrapped collection’s enumerator

 public IEnumerator GetEnumerator( )
  {
      // What should I return here ?
       //like the following ?
        yield return names[0];
        yield return names[1];
        yield return names[2];
      ....

       (or)
        foreach(string str in names)
        {
            yield return str;
         }                  

  }

}

(第 2 点)

•Via an iterator using yield return.(This point was well explained 
 by Marc Gravell)

第 3 点

By instantiating your own IEnumerator/IEnumerator<T> implementation*

第 3 点在这里代表什么?,因为没有例子,我没有得到那个。这是否意味着,我可以构建自定义枚举器..(对吗?)。我的问题是,当预构建枚举器/枚举器足以进行迭代时(作为初学者,我不应该盲目地确认这一点)为什么我应该照顾自定义的?示例将澄清我的疑问。

感谢您阅读这个冗长的故事和友好的回应。

4

4 回答 4

3

(第 1 点)您可以将 GetEnumerator 的调用链接到其他集合枚举器:

public class PrimeNumbers : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        var primes = new List<int> { 2, 3, 5, 7, 11 };
        return primes.GetEnumerator();
    }
}

(第 2 点)类似于代码中的示例

public IEnumerator GetEnumerator()
{
    var primes = new List<int> { 2, 3, 5, 7, 11 };
    foreach (var number in primes)
    {
        yield return number;
    }
}

或者用逻辑放置枚举器:

public class PrimeNumbers : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        for(int i=2; ;i++)
        {
            if(IsPrime(i))
            {
                yield return i;
            }
        }
    }
}

(第 3 点)您想要实现自己的 Enumerator 的情况并不多,但例如您可以查找无限的值集,例如素数:

public class PrimeNumbers : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        return new MyEnumerator();
    }
}

public class MyEnumerator : IEnumerator
{
    private int lastPrimeNumber = 1;

    public bool MoveNext()
    {
        lastPrimeNumber = /* some logic that find the next prime */;
        return true; // There is always next prime
    }

    public void Reset()
    {
        lastPrimeNumber = 1;
    }

    public object Current
    {
        get { return lastPrimeNumber; }
    }
}

一个使用示例可能是:

public void PrintAllPrimes()
{
    var numbers = new PrimeNumbers();

    // This will never end but it'll print all primes until my PC crash
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

我能想到的优点和缺点:

  • 第 1 点:这是枚举项目的最简单方法,但它需要提前知道所有项目
  • 第2点:当枚举中有一些逻辑时它是可读的,它也是惰性的,所以在实际请求之前不需要计算一个项目
  • 第 3 点:重用最简单,但可读性较差(在 MoveNext 中计算项目,但实际上从 Current 属性返回)。
于 2009-10-20T21:15:35.063 回答
2

这是一个示例:(注意:这是简化的,不是线程安全的示例)

public class PersonCollection : IEnumerable
{
    private ArrayList alPers = new ArrayList();
    public IEnumerator GetEnumerator() { return new myTypeEnumerator(this); }
    public class myTypeEnumerator : IEnumerator
    {
        int nIndex;
        PersonCollection pers;
        private int count { get { return pers.alPers.Count; } }
        public myTypeEnumerator(PersonCollection myTypes) 
         { pers = myTypes; nIndex = -1; }
        public bool MoveNext() { return nIndex <= count && ++nIndex < count; }
        // MovePrev() not strictly required
        public bool MovePrev() { return (nIndex > -1 && --nIndex > 0 ); }
        public object Current { get { return (pers[nIndex]); } }
        public void Reset() { nIndex = -1; }
    }
}

编辑:修复@Joren 提出的与移动上一个索引值低于-1 相关的问题。当框架作为 foreach 实现的一部分调用时,MoveNext() 不需要此修复,因为在这种情况下,如果 MoveNext() 返回 false,枚举器实例将终止。但是,如果客户端手动调用 MoveNext(),枚举器不会终止,因此它也需要修复。

另请注意:您如何在此事物中实现细节取决于您,并且将取决于它如何在内部管理状态。例如,如果保存引用的内部“桶”是一个链表,而不是一个 ArrayList,那么 MoveNext() 可以通过将 Current 字段更改为指向旧当前的 nextItem 属性来实现......

于 2009-10-20T21:07:28.620 回答
1

你说的1实际上是2。

对于 1,它会更像

public IEnumerator GetEnumerator() {
    return names.GetEnumerator();
}

对于第 3 点,这意味着让 GetEnumerator 做一些真正的工作:为您定义的集合手动实现一个 Enumerator。

于 2009-10-20T21:09:58.460 回答
0

“如果该类正在“包装”另一个集合,则通过返回被包装的集合的枚举数”意味着:

class StringCollections{
    //A class is wrapping another collection
    string[] names=new string {“Jon Skeet”,”Hamish Smith”,
                                ”Marc Gravell”,”Jrista”,”Joren”};
    public IEnumerator GetEnumerator() { return names.GetEnumerator(); }
}
于 2009-10-20T21:25:34.527 回答