1

When reading a post some points were given without example :

To implement IEnumerable / IEnumerable, you must provide an enumerator :

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

•Via an iterator using yield return.

•By instantiating your own IEnumerator/IEnumerator implementation

(My baby mind interprets it as)

(Point 1)

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

Will it mean ..

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

  }

}

(Point 2)

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

Point 3

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

What does point 3 represent here?,since no example,i did not get that one. Does it mean,i can build custom enumerator ..(right?).My question here is when prebuild enumerator/enumerators are sufficient (as a beginner i should not blindly confirm this) for iterations why should i look after the custom one ?Nice example will clarify my doubt.

Thanks for reading this long winded story and kind response.

4 Answers 4

3

(point 1) You can chain the call of GetEnumerator to other collection enumerator:

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

(point 2) Similar to example in your code

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

Or place enumerator with logic:

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

(Point 3) There are not many cases where you want to implement your own Enumerator, but for example you can look for an infinite set of values, e.g. prime numbers:

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

A usage example could be:

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

Pros & Cons I can think of:

  • Point 1: It's the simplest way to enumerate items but it requires knowing all items in advance
  • Point 2: It's readable when there's some logic in the enumeration, it's also lazy so there is no need to compute an item until it's actually requested
  • Point 3: It's simplest to reuse but it is less readable (calculate item in MoveNext but actually return from Current property).
1
  • Thanks you Elisha for sharing info
    – user193276
    Commented Oct 20, 2009 at 21:18
2

Here's an example: (NOTE: This is simplified, not thread safe example)

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

EDIT: to fix issue raised by @Joren below related to Move Previous taking index value below -1. When called by framework as part of foreach implementation, MoveNext() would not need this fix because in that case if MoveNext() returns false, enumerator instance will terminate. However, if client manually calls MoveNext(), enumerator would not terminate, so it also needs the fix.

Also NOTE: how you implement the details inside this thing is up to you, and will be dependant on how it internally manages state. For example if the iunternal "bucket" that holds the references was a linked list, instead of an ArrayList, then the MoveNext() might be implemented by just changing a Current field to point to the old current's nextItem property...

5
  • Thanks for spending your time. :)
    – user193276
    Commented Oct 20, 2009 at 21:20
  • Your MoveNext and MovePrev are weird. If I create that enumerator, do MovePrev() twice, and MoveNext() once, nIndex becomes -1 but the call returns true. If I'm not mistaken, both methods should change nIndex and then return 0 <= nIndex && nIndex < pers.alPers.Count.
    – Joren
    Commented Oct 20, 2009 at 21:20
  • @Joren, bug, corrected MovePrev must compare to zero, not the count. But you don't need top compare index against the "other" end of the range... It can never get less than -1 or greater than count... as as soon as it returns false, the enumeration ends... Commented Oct 20, 2009 at 21:27
  • @Joren, on second thought, I see you're calling Move methods explicitly... and yes, in that case, you are right. But this pattern is designed to be used by the framework when client code uses foreach ... in which case the framework generated IL code is calling MoveNext().. and what you describe can't happen... But if MovePrev() is in there, it should be coded to handle your scenario... Commented Oct 20, 2009 at 21:30
  • I think enumerator code should be robust enough to handle this case. MovePrev won't even ever be called by foreach at all. If you're including it, it should work appropriately.
    – Joren
    Commented Oct 20, 2009 at 21:34
1

What you said for 1 would actually be for 2.

For 1, it'd be more like

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

For point 3, it'd mean to make GetEnumerator do some real work: Implementing, by hand, an Enumerator for the collection you defined.

0
0

"If the class is "wrapping" another collection, by returning thewrapped collection's enumerator" means:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.