Зачем мне нужен IEnumerator.Ток в классе, реализующем IEnumerator?
У меня есть класс, который реализует IEnumerator<string>
. Смотрите ниже:
public class MyClass : IEnumerator<string>
{
public bool MoveNext()
{
//....
}
//Implement other required methods....
//Confusion lies below:
public string Current { get { return this.CurrentLine; } }
//Why do I need to implement IEnumerator.Current?! In my tests, it's not even called during my iteration
object IEnumerator.Current { get { throw new NotImplementedException(); } }
}
Кроме того, что .Текущее свойство существует как на интерфейсе IEnumerator<T>
, так и на интерфейсе IEnumerator
(который IEnumerator<T>
наследует), какой смысл его реализовывать? Как видно выше, это даже не называется.
3 ответа:
IEnumerator<T>
реализуетIEnumerator
, поэтому на самом базовом уровне вы должны выполнить контракт.В частности, почему-что происходит, если кто-то делает это:
((IEnumerator)yourInstance).Current
Они (как правило) должны ожидать получить слабо типизированную копию того же значения/ссылки, возвращенную из реализации
IEnumerator<T>
. Поэтому в большинстве случаев просто возвращайтеthis.Current
и не беспокойтесь об этом:)(FYI-returning
this.Current
также является хорошей практикой, потому что он следует за DRY и SRP-пусть сильно типизированная версия Current разберитесь с деталями реализации того, что на самом деле является текущим.)
Причина в том, что
IEnumerator<T>
наследуетIEnumerator
, поэтому, когда вы наследуете отIEnumerator<T>
, вы неявно также наследуете отIEnumerator
. Если вы рекламируете интерфейс, вы также должны предоставить реализацию для этого интерфейса, даже если вы никогда не собираетесь его использовать.
Компилятор требует, чтобы вы реализовали все виртуалы, потому что он не может знать, какие из них будут вызваны, когда какая-то непредвиденная сборка загрузит вашу сборку в какой-то момент в неизвестном будущем. Наследуя от интерфейса, вы "подписываете контракт", который обещает, что вы будете реализовывать все его члены. Компилятор придерживается этого соглашения, чтобы другие сборки могли полагаться на него.
Функция интерфейса предназначена для того, чтобы ваш класс мог скажите любому другому собранию, в любом месте, в любое время: "это то, что вы можете попросить меня сделать". Если вы хотите объявить меньшую возможность, определите новый интерфейс, который предоставляет только частичную функциональность, которую вы хотите, и реализуйте ее вместо этого.
Конечно, все это-материал промышленной прочности. Это больше, чем вам нужно для вашего кода прямо сейчас. Но C# должен быть полезен для серьезных вещей, а не только для игрушек.
Что касается двух различных, почти идентичных переопределений: вы должны переопределяет оба текущих свойства, поскольку они существенно различны: одно-это универсальный возвращаемый объект T;другое-не универсальный возвращаемый объект. Вы всегда можете рассматривать ссылку на строку как ссылку на объект, но это не идет в обоих направлениях. А как насчет типов ценностей? T не ограничен быть классом. Конечно, компилятор мог бы гипотетически вычислить все это для вас и позволить вам сорваться с крючка в тех случаях, когда эти два понятия взаимозаменяемы, но это не так, и я не уверен, что это должно быть. Если ты хотите C++, вы знаете, где его найти.