Поведение метода Reset списка <T>.Enumerator

Следуя двум методам (один использует IEnumerator<int>, а другой использует List<int>.Enumerator), хотя выглядит одинаково, дает разные результаты.

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

Есть несколько вопросов, которые ясно объясняют это поведение, вы можете проверить здесь здесь, здесь, и здесь.

У меня все еще есть два сомнения

  • Почему List.Enumerator не выбрасывает "NotSupportedException" для Reset?
  • Почему Reset был реализован явно и неявно, как MoveNext и Current?

Ответ 1

Почему List.Enumerator не бросает "NotSupportedException" для Reset?

Потому что у Microsoft не было машины времени, чтобы предвидеть, что произойдет через 5 лет. Сильным толчком к выводу типа был Linq, он просто не был на дорожной карте еще в конце 1990-х годов, когда вначале работали дженерики. Проблема без бокса просто не проблема без него.

Почему Reset был реализован явно и неявно, как MoveNext и Current?

Поскольку вы не можете наследовать метод интерфейса, его можно скрыть. Этот IEnumerator имеет метод Reset() - еще одна проблема с машиной времени, это было решено еще в 1995 году, когда была разработана COM Automation. Примерно еще 5-летний разрыв между выбором и последствием:).NET должен был обеспечить достойное сопоставление между COM-итераторами и .NET-итераторами, чтобы иметь шанс на бой для принятия.

Как вы можете сказать по ссылке, еще одна возможность в итераторах COM - клонирование. Который стал стимулом для интерфейса ICloneable, еще одним очень неприятным интерфейсом в .NET. Это было слишком много проблем для реализации в их родовых собратьях, только это не универсальные сборщики коллекции.

Microsoft имеет сложную работу, каждое дизайнерское решение - это то, с чем им нужно жить вечно. Нам намного проще, мы просто не можем использовать Reset:)

Ответ 2

Почему List.Enumerator не бросает "NotSupportedException" для Reset?

Почему? List<T> - это тип, для которого тривиально реализовать Reset, так почему бы не реализовать его?

Почему Reset был реализован явно и неявно, как MoveNext и Current?

Я думаю, потому что Reset обычно считается ошибкой. Но он существует, поэтому его нужно каким-то образом реализовать. И поэтому скрытие его с помощью явной реализации интерфейса имеет смысл, оно говорит "вы, вероятно, не должны использовать это".