167

I know how to implement the non generic IEnumerable, like this:

using System;
using System.Collections;

namespace ConsoleApplication33
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObjects myObjects = new MyObjects();
            myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
            myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };

            foreach (MyObject x in myObjects)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }

            Console.ReadLine();
        }
    }

    class MyObject
    {
        public string Foo { get; set; }
        public int Bar { get; set; }
    }

    class MyObjects : IEnumerable
    {
        ArrayList mylist = new ArrayList();

        public MyObject this[int index]
        {
            get { return (MyObject)mylist[index]; }
            set { mylist.Insert(index, value); }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return mylist.GetEnumerator();
        }
    }
}

However I also notice that IEnumerable has a generic version, IEnumerable<T>, but I can't figure out how to implement it.

If I add using System.Collections.Generic; to my using directives, and then change:

class MyObjects : IEnumerable

to:

class MyObjects : IEnumerable<MyObject>

And then right click on IEnumerable<MyObject> and select Implement Interface => Implement Interface, Visual Studio helpfully adds the following block of code:

IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
    throw new NotImplementedException();
}

Returning the non generic IEnumerable object from the GetEnumerator(); method doesn't work this time, so what do I put here? The CLI now ignores the non generic implementation and heads straight for the generic version when it tries to enumerate through my array during the foreach loop.

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
JMK
  • 27,273
  • 52
  • 163
  • 280

7 Answers7

190

If you choose to use a generic collection, such as List<MyObject> instead of ArrayList, you'll find that the List<MyObject> will provide both generic and non-generic enumerators that you can use.

using System.Collections;

class MyObjects : IEnumerable<MyObject>
{
    List<MyObject> mylist = new List<MyObject>();

    public MyObject this[int index]  
    {  
        get { return mylist[index]; }  
        set { mylist.Insert(index, value); }  
    } 

    public IEnumerator<MyObject> GetEnumerator()
    {
        return mylist.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
huysentruitw
  • 27,376
  • 9
  • 90
  • 133
Monroe Thomas
  • 4,962
  • 1
  • 17
  • 21
88

You probably do not want an explicit implementation of IEnumerable<T> (which is what you've shown).

The usual pattern is to use IEnumerable<T>'s GetEnumerator in the explicit implementation of IEnumerable:

class FooCollection : IEnumerable<Foo>, IEnumerable
{
    SomeCollection<Foo> foos;

    // Explicit for IEnumerable because weakly typed collections are Bad
    System.Collections.IEnumerator IEnumerable.GetEnumerator()
    {
        // uses the strongly typed IEnumerable<T> implementation
        return this.GetEnumerator();
    }

    // Normal implementation for IEnumerable<T>
    IEnumerator<Foo> GetEnumerator()
    {
        foreach (Foo foo in this.foos)
        {
            yield return foo;
            //nb: if SomeCollection is not strongly-typed use a cast:
            // yield return (Foo)foo;
            // Or better yet, switch to an internal collection which is
            // strongly-typed. Such as List<T> or T[], your choice.
        }

        // or, as pointed out: return this.foos.GetEnumerator();
    }
}
Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
user7116
  • 63,008
  • 17
  • 141
  • 172
  • 3
    This is a good solution if SomeCollection does not already implement IEnumerable, or if you need to transform or do other processing on each element before it is returned in the enumeration sequence. If neither of these conditions are true, then it is probably more efficient to just return this.foos.GetEnumerator(); – Monroe Thomas Jul 02 '12 at 15:55
  • @MonroeThomas: agreed. No clue what collection the OP is using. – user7116 Jul 02 '12 at 15:58
  • 11
    Good point. But implementing `IEnumerable` is redundant (`IEnumerable` already inherits from it). – rsenna Dec 18 '14 at 17:21
  • @rsenna that's true, however keep in mind even .NET interfaces like IList redundantly implement interfaces, it's for readability - public interface IList : ICollection, IEnumerable – David Klempfner Dec 11 '18 at 09:31
  • @Backwards_Dave I actually (still) think it makes less readable, since it adds unnecessary noise, but I understand what you said, and think it's a valid opinion. – rsenna Dec 11 '18 at 11:03
26

Why do you do it manually? yield return automates the entire process of handling iterators. (I also wrote about it on my blog, including a look at the compiler generated code).

If you really want to do it yourself, you have to return a generic enumerator too. You won't be able to use an ArrayList any more since that's non-generic. Change it to a List<MyObject> instead. That of course assumes that you only have objects of type MyObject (or derived types) in your collection.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
  • 2
    +1, it should be noted that the preferred pattern is to implement the generic interface and explicitly implement the non-generic interface. Yield return is the most natural solution to the generic interface. – user7116 Jul 02 '12 at 15:45
  • 7
    Unless the OP is going to be doing some processing in the enumerator, using yield return just adds the overhead of another state machine. The OP should just return an enumerator provided by an underlying generic collection. – Monroe Thomas Jul 02 '12 at 15:51
  • 1
    @MonroeThomas: You're right. I didn't read the question very carefully before writing about `yield return`. I wrongly assumed that there were some custom processing. – Anders Abel Jul 02 '12 at 15:55
5

If you work with generics, use List instead of ArrayList. The List has exactly the GetEnumerator method you need.

List<MyObject> myList = new List<MyObject>();
Amiram Korach
  • 13,056
  • 3
  • 28
  • 30
0

make mylist into a List<MyObject>, is one option

ColWhi
  • 1,077
  • 6
  • 16
0

Note that the IEnumerable<T> allready implemented by the System.Collections so another approach is to derive your MyObjects class from System.Collections as a base class (documentation):

System.Collections: Provides the base class for a generic collection.

We can later make our own implemenation to override the virtual System.Collections methods to provide custom behavior (only for ClearItems, InsertItem, RemoveItem, and SetItem along with Equals, GetHashCode, and ToString from Object). Unlike the List<T> which is not designed to be easily extensible.

Example:

public class FooCollection : System.Collections<Foo>
{
    //...
    protected override void InsertItem(int index, Foo newItem)
    {
        base.InsertItem(index, newItem);     
        Console.Write("An item was successfully inserted to MyCollection!");
    }
}

public static void Main()
{
    FooCollection fooCollection = new FooCollection();
    fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}

Please note that driving from collection recommended only in case when custom collection behavior is needed, which is rarely happens. see usage.

Shahar Shokrani
  • 7,598
  • 9
  • 48
  • 91
-1

.... and do not forget the using if you are blocked by

    IEnumerator IEnumerable.GetEnumerator()
    {
        // uses the strongly typed IEnumerable<T> implementation
        return GetEnumerator();
    }

add this:

using System.Collections;

also:

: IEnumerable<string>

is sufficient has it is embedding the IEnumerable:

public interface IEnumerable<out T> : IEnumerable

... also beware of Syntax jokes between the A in enumrAble and the o in enumratOr which are both used in this pattern ! Nice dangerous line:

IEnumerator IEnumerable.GetEnumerator()
Thierry Brémard
  • 625
  • 1
  • 5
  • 14