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