Зачем реализовывать 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
интерфейс); но это просто пришло мне в голову, и я не вижу каких-либо очевидных проблем, которые возникнет при таком подходе.
В общем, если я могу написать умеренное количество кодаодин раз чтобы избавить себя от необходимости писать небольшое количество кодамного временидля меня это того стоит.
* Это правильная терминология для использования? Я всегда не решаюсь сказать, что один интерфейс «наследует» от другого, так как кажется, что он не отражает должным образом отношения между ними. Но, возможно, это правильно.