JMK
JMK

Reputation: 28079

How do I implement IEnumerable<T>

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.

Upvotes: 175

Views: 190128

Answers (7)

Thierry Br&#233;mard
Thierry Br&#233;mard

Reputation: 899

.... 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()

Upvotes: 0

user7116
user7116

Reputation: 64138

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

Upvotes: 95

Shahar Shokrani
Shahar Shokrani

Reputation: 8762

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.

Upvotes: 0

Monroe Thomas
Monroe Thomas

Reputation: 5042

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

Upvotes: 196

Anders Abel
Anders Abel

Reputation: 69280

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.

Upvotes: 26

Amiram Korach
Amiram Korach

Reputation: 13296

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

Upvotes: 5

ColWhi
ColWhi

Reputation: 1077

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

Upvotes: 0

Related Questions