Зачем мне нужен IEnumerator.Current в классе, реализующем IEnumerator <T>? - PullRequest
7 голосов
/ 02 марта 2011

У меня есть класс, который реализует 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(); } }

}   

Помимо того факта, что свойство .Current существует как в интерфейсе IEnumerator<T>, так и в интерфейсе IEnumerator (который наследуется IEnumerator<T>), какой смысл его реализовывать? Как видно выше, он даже не называется.

Ответы [ 3 ]

15 голосов
/ 02 марта 2011

IEnumerator<T> реализует IEnumerator, поэтому на самом базовом уровне вы должны выполнить контракт.

В частности, почему - что произойдет, если кто-то сделает это:

((IEnumerator)yourInstance).Current

Они (обычно) должны ожидать получить свободно напечатанную копию того же значения / ссылки, возвращенного из реализации IEnumerator<T>. Так что в большинстве случаев просто возвращайте this.Current и не беспокойтесь об этом:)

(К вашему сведению - возвращение this.Current также является хорошей практикой, поскольку оно следует за DRY и SRP - пусть строго типизированная версия Current имеет дело с деталями реализации того, чем на самом деле является Current.)

3 голосов
/ 02 марта 2011

Причина в том, что IEnumerator<T> наследует IEnumerator, поэтому, когда вы наследуете от IEnumerator<T>, вы неявно также наследуете от IEnumerator.Если вы рекламируете интерфейс, вы должны также предоставить реализацию для этого интерфейса, даже если вы никогда не намереваетесь его использовать.

1 голос
/ 02 марта 2011

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

Цель функции интерфейса - дать возможность вашему классу сообщать любой другой сборке, в любом месте, в любое время, «это то, что вы можете попросить меня сделать».Если вы хотите объявить о меньшей возможности, определите новый интерфейс, который предоставляет только те функции, которые вам нужны, и используйте его вместо этого.

Конечно, все это вещи промышленного уровня.Это больше, чем вам нужно для вашего кода прямо сейчас.Но C # предназначен для того, чтобы делать серьезные вещи, а не только игрушки.

Что касается двух разных, почти идентичных переопределений: вы должны переопределить оба свойства Current, потому что они существенно различаются: один - это универсальный возвращающий T;другой не является универсальным возвращающим объектом.Вы всегда можете рассматривать ссылку String как ссылку на Object, но это не идет в обе стороны.А как насчет типов значений?T не обязан быть классом.Несомненно, компилятор мог бы гипотетически выяснить все это для вас и позволить вам сорваться с крючка в тех случаях, когда оба они взаимозаменяемы, но это не так, и я не уверен, что это должно быть.Если вы хотите C ++, вы знаете, где его найти.

...