rookie
rookie

Reputation: 2923

Why I can't use OrderBy despite having GetEnumerator and foreach working well?

I've implemented the GetEnumerator method for a simple class and was surprised that I couldn't order the enumerator with linq (a call to this.OrderBy(x => x) is invalid). Can someone please explain what's going on here? Am I doing something wrong or are enumerators only intended to be iterated over?

class Test
{
    private Dictionary<int, string> dict
        = new Dictionary<int, string>();

    public IEnumerator<int> GetEnumerator()
    {
        return dict.Keys.GetEnumerator();
    }

    public Test()
    {
        dict[1] = "test";
        dict[2] = "nothing";
    }

    public IEnumerable<int> SortedKeys
    {
        get { return this.OrderBy(x => x); } // illegal!
    }

    public void Print()
    {
        foreach(var key in this)
            Console.WriteLine(dict[key]);
    }
}

Upvotes: 3

Views: 787

Answers (3)

Dennis_E
Dennis_E

Reputation: 8894

OrderBy() is an extension method on IEnumerable<T>.
Your class does not implement IEnumerable<T>.

foreach still works, because it does not require you to implement IEnumerable<T>; it only requires that there is a method GetEnumerator().

So all you need to do is add:

class Test : IEnumerable<int>

and provide the implementation for the non-generic IEnumerable:

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

Upvotes: 1

Patrick Hofman
Patrick Hofman

Reputation: 157038

You have to implement the interface IEnumerable<int> in order for the this.OrderBy to work, how else should it know this can enumerate ints?

OrderBy requires this to implement IEnumerable<T>. It doesn't know your GetEnumerator method is actually an attempt to comply to the interface.

foreach just requires a GetEnumerator() method, no interface implementatio needed.

// put in the interface
class Test : IEnumerable<int>
{
    private Dictionary<int, string> dict
        = new Dictionary<int, string>();

    public IEnumerator<int> GetEnumerator()
    {
        return dict.Keys.GetEnumerator();
    }

    public Test()
    {
        dict[1] = "test";
        dict[2] = "nothing";
    }

    public IEnumerable<int> SortedKeys
    {
        get { return this.OrderBy(x => x); } // illegal!
    }

    public void Print()
    {
        foreach (var key in this)
            Console.WriteLine(dict[key]);
    }

    // this one is required according to the interface too
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Upvotes: 7

Mat&#237;as Fidemraizer
Mat&#237;as Fidemraizer

Reputation: 64943

An enumerator is an iterator. It's just an interface that tells the runtime or custom code on how to move to a next element in some sequence, reset the iteration to the first element again or get current element in the iteration.

That is, an enumerator isn't enumerable. An enumerable can create an enumerator to let other code enumerate the enumeration.

In order to be able to call a LINQ extension method you need the object to be enumerable. Your Test class doesn't implement IEnumerable<T> (LINQ extension method signatures look like this: public static IEnumerable<T> Whatever<T>(this IEnumerable<T> someEnumerable)).

Since I want to apply DRY principle on myself (Don't Repeat Yourself), if you want to know how to implement IEnumerable<T> you should look at the following Q&A: How do I implement IEnumerable<T>.

Upvotes: 1

Related Questions