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