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