raven
raven

Reputation: 2564

Simple way to implement a Collection?

I am developing a collection class, which should implement IEnumerator and IEnumerable.

In my first approach, I implemented them directly. Now I have discovered the yield keyword, and I have been able to simplify everything a whole lot substituting the IEnumerator/IEnumerable interfaces with a readonly property Values that uses yield to return an IEnumerable in a loop.

My question: is it possible to use yield in such a way that I could iterate over the class itself, without implementing IEnumerable/IEnumerator?

I.e., I want to have a functionality similar to the framework collections:

List<int> myList = new List<int>();
foreach (int i in myList)
{
    ...
}

Is this possible at all?

Update: It seems that my question was badly worded. I don't mind implementing IEnumerator or IEnumerable; I just thought the only way to do it was with the old Current/MoveNext/Reset methods.

Upvotes: 2

Views: 2382

Answers (2)

Jodrell
Jodrell

Reputation: 35716

You could do somthing like this, but why? IEnumerator is already simple.

Interface MyEnumerator<T>
{
    public T GetNext();
}

public static class MyEnumeratorExtender
{
    public static void MyForeach<T>(this MyEnumerator<T> enumerator,
        Action<T> action)
    {
        T item = enumerator.GetNext();
        while (item != null)
        {
            action.Invoke(item);
            item = enumerator.GetNext();
        }
    }
}

I'd rather have the in keyword and I wouldn't want to rewrite linq.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500525

You won't have to implement IEnumerable<T> or IEnumerable to get foreach to work - but it would be a good idea to do so. It's very easy to do:

public class Foo : IEnumerable<Bar>
{
    public IEnumerator<Bar> GetEnumerator()
    {
        // Use yield return here, or 
        // just return Values.GetEnumerator()
    }

    // Explicit interface implementation for non-generic
    // interface; delegates to generic implementation.
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

The alternative which doesn't implement IEnumerable<T> would just call your Values property, but still providing a GetEnumerator() method:

public class Foo
{
    public IEnumerator<Bar> GetEnumerator()
    {
        // Use yield return here, or 
        // just return Values.GetEnumerator()
    }
]

While this will work, it means you won't be able to pass your collection to anything expecting an IEnumerable<T>, such as LINQ to Objects.

It's a little-known fact that foreach will work with any type supporting a GetEnumerator() method which returns a type with appropriate MoveNext() and Current members. This was really to allow strongly-typed collections before generics, where iterating over the collection wouldn't box value types etc. There's really no call for it now, IMO.

Upvotes: 9

Related Questions