Поведение метода 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
обычно считается ошибкой. Но он существует, поэтому его нужно каким-то образом реализовать. И поэтому скрытие его с помощью явной реализации интерфейса имеет смысл, оно говорит "вы, вероятно, не должны использовать это".