3

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]);
    }
}
quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
rookie
  • 2,783
  • 5
  • 29
  • 43
  • 3
    You need to implement [`IEnumerable`](https://msdn.microsoft.com/en-us/library/9eekhta0.aspx) – cbr Jul 09 '15 at 13:35

3 Answers3

7

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();
    }
}
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
1

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();
}
Dennis_E
  • 8,751
  • 23
  • 29
1

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

Community
  • 1
  • 1
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206