Question

Following two methods (one uses IEnumerator<int>, and other uses List<int>.Enumerator) even though looks identical produces different results.

static void M1()
{
  var list = new List<int>() { 1, 2, 3, 4 };
  IEnumerator<int> iterator = list.GetEnumerator();
  while (iterator.MoveNext())
  {
     Console.Write(iterator.Current);
  }
  iterator.Reset();
  while (iterator.MoveNext())
  {
      Console.Write(iterator.Current);
  }
}
static void M2()
{
  var list = new List<int>() { 1, 2, 3, 4 };
  //Here the iterator will be List<int>.Enumerator (which is a struct)
  var iterator = list.GetEnumerator();
  while (iterator.MoveNext())
  {
     Console.Write(iterator.Current);
  }
  //This will not work, as Reset method was implemented explicitly
  //iterator.Reset();

  //So casting it to IEnumerator is required
  //which will lead to boxing and other issues of struct and interface
  ((IEnumerator<int>)iterator).Reset();

  //Following loop will NOT work
  while (iterator.MoveNext())
  {
    Console.Write(iterator.Current);
  }
}

There are couple of questions which clearly explains this behavior, you can check them here, here, and here.

I still have following two doubts

  1. Why List.Enumerator does not throw "NotSupportedException" for Reset?
  2. Why Reset was implemented explicitly and not implicitly like MoveNext and Current?
Was it helpful?

Solution

Why List.Enumerator does not throw "NotSupportedException" for Reset?

Because Microsoft did not have a time-machine to foresee what was going to happen 5 years later. The strong impetus behind type inference was Linq, it just wasn't on the road-map back in the late 1990s when generics were first being worked on. The boxing problem just isn't an issue without it.

Why Reset was implemented explicitly and not implicitly like MoveNext and Current?

Because you can't un-inherit an interface method, you can only hide it. That IEnumerator has a Reset() method is yet another time-machine problem, this was decided back in 1995 when COM Automation was designed. Roughly another 5 year gap between choice and consequence :) .NET had to provide a decent mapping between COM iterators and .NET iterators to have a fighting chance to be adopted.

As you can tell from the link, another capability in COM iterators is cloning. Which was the impetus behind the ICloneable interface, another very troublesome interface in .NET. That one was too much trouble to implement in their generic brethren, only the non-generic collection enumerators implement it.

Microsoft has a tough job, every design decision is one they have to live with forever. Ours is much easier, we can simply not use Reset :)

OTHER TIPS

Why List.Enumerator does not throw "NotSupportedException" for Reset?

Why should it? List<T> is a type for which it's trivial to implement Reset, so why not implement it?

Why Reset was implemented explicitly and not implicitly like MoveNext and Current?

I think it's because Reset is generally considered to be a mistake now. But it does exist, so it has to be implemented somehow. And so hiding it using explicit interface implementation makes sense, it says “you probably shouldn't be using this”.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top