Зачем реализовывать IEnumerable (T), если я могу просто определить ОДИН GetEnumerator?

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

Так что да, считай меня убежденным: это была бы плохая идея.

И просто для того, чтобы хоть как-то спасти мое достоинство: я мог бы подыграть ему ради аргумента, но мне никогда не приходило в голову эту идею с самого начала - просто любопытно услышать, что другие говорят об этом. Честный.

Прежде чем отклонить этот вопрос как абсурдный, я прошу вас рассмотреть следующее:

IEnumerable<T> наследуется от *IEnumerable, что означает, что любой тип, который реализуетIEnumerable<T> как правило, должны реализоватьи то и другое IEnumerable<T>.GetEnumerator а также (Явно)IEnumerable.GetEnumerator, Это в основном составляет стандартный код.Вы можетеforeach над любым типом, который имеетGetEnumerator метод, если этот метод возвращает объект некоторого типа сMoveNext метод иCurrent имущество. Так что если ваш тип определяетодин метод с подписьюpublic IEnumerator<T> GetEnumerator()это законно перечислять, используяforeach.Очевидно, что существует много кода, который требуетIEnumerable<T> интерфейс - например, в основном все методы расширения LINQ. К счастью, чтобы перейти от типа, который вы можетеforeach наIEnumerable<T> тривиально с использованием автоматической генерации итератора, который C # поставляет черезyield ключевое слово.

Итак, собрав все это вместе, у меня возникла безумная идея: что если я просто определю свой собственный интерфейс, который выглядит следующим образом:

public interface IForEachable<T>
{
    IEnumerator<T> GetEnumerator();
}

затем всякий раз, когда я определяю тип, который я хочу перечислить, я реализуюэтот интерфейс вместоIEnumerable<T>, устраняя необходимость реализации двухGetEnumerator методы (один явный). Например:

class NaturalNumbers : IForEachable<int>
{
   public IEnumerator<int> GetEnumerator()
   {
       int i = 1;
       while (i < int.MaxValue)
       {
           yield return (i++);
       }
   }

   // Notice how I don't have to define a method like
   // IEnumerator IEnumerable.GetEnumerator().
}

в заключение, чтобы сделать этот тип совместимым с кодом, которыйделает ожидатьIEnumerable<T> интерфейс, я могу просто определить метод расширения, чтобы перейти от любогоIForEachable<T> дляIEnumerable<T> вот так:

public static class ForEachableExtensions
{
    public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
    {
        foreach (T item in source)
        {
            yield return item;
        }
    }
}

Мне кажется, что это позволяет мне проектировать типы, которые могут использоваться всеми способами в качестве реализацийIEnumerable<T>, но без этого надоедливого явногоIEnumerable.GetEnumerator реализация в каждом.

Например:

var numbers = new NaturalNumbers();

// I can foreach myself...
foreach (int x in numbers)
{
    if (x > 100)
        break;

    if (x % 2 != 0)
        continue;

    Console.WriteLine(x);
}

// Or I can treat this object as an IEnumerable<T> implementation
// if I want to...
var evenNumbers = from x in numbers.AsEnumerable()
                  where x % 2 == 0
                  select x;

foreach (int x in evenNumbers.TakeWhile(i => i <= 100))
{
    Console.WriteLine(x);
}

Что вы, ребята, думаете об этой идее? Я что-то упускаю, почему это будет ошибкой?

Я понимаю, что это, вероятно, кажется слишком сложным решением для того, что не так уж важно для начала (я сомневаюсь никогодействительно заботится так много о необходимости явно определитьIEnumerable интерфейс); но это просто пришло мне в голову, и я не вижу каких-либо очевидных проблем, которые возникнет при таком подходе.

В общем, если я могу написать умеренное количество кодаодин раз чтобы избавить себя от необходимости писать небольшое количество кодамного временидля меня это того стоит.

* Это правильная терминология для использования? Я всегда не решаюсь сказать, что один интерфейс «наследует» от другого, так как кажется, что он не отражает должным образом отношения между ними. Но, возможно, это правильно.

Ответы на вопрос(6)

Ваш ответ на вопрос