自定义枚举的需要点

时间:2009-10-20 21:01:49

标签: c# collections enumeration

在阅读帖子时,一些要点没有示例

要实现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 个答案:

答案 0 :(得分: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点) 您想要实现自己的枚举器的情况并不多,但例如,您可以查找一组无限的值,例如:素数:

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);
    }
}

优点&amp;我能想到的缺点:

  • 第1点:这是枚举项目的最简单方法,但它需要 提前知道所有项目
  • 第2点:当枚举中有一些逻辑时,它是可读的,它是 也很懒,所以没有必要 计算一个项目,直到它实际上 请求的
  • 第3点:重用最简单,但可读性较差(计算项目 在MoveNext但实际上从 目前的财产)。

答案 1 :(得分: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引发的问题,下面与Move Previous相关,索引值低于-1。当框架作为foreach实现的一部分调用时,MoveNext()不需要此修复,因为在这种情况下如果MoveNext()返回false,则枚举实例将终止。但是,如果客户端手动调用MoveNext(),枚举器将不会终止,因此它也需要修复。

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

答案 2 :(得分:1)

你所说的1实际上是2。

对于1,它更像是

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

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

答案 3 :(得分: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(); }
}