Это плохая практика внедрения бесконечных перечислителей и перечислимых элементов?
У меня есть следующий класс:
class CopyProvider<T> where T: IMyCloneable
{
private readonly T _original;
public CopyProvider(T original) => _original = original;
public T Current { get; private set; }
public bool MoveNext()
{
Current = _original.Clone();
return true;
}
}
Теперь мне интересно, было бы хорошей практикой объявить этот класс как реализацию IEnumerator<T>
, С синтаксической точки зрения CopyProvider
соответствует ли требованиям, но будет ли это нарушением IEnumerator
семантика?
В качестве альтернативы я также мог бы написать следующий метод:
IEnumerable<T> ProvideCopies<T>(T original) where T: IMyCloneable
{
while(true)
yield return original.Clone();
}
Это было бы намного удобнее, действительно. Но опять же, я сталкиваюсь с тем же вопросом: будет ли это использование IEnumerable
нарушить семантику интерфейса?
Или я должен рассмотреть реализацию отдельной объектной структуры с InfiniteEnumerator
и / или InfiniteEnumerable
? Тем не менее, это лишило бы меня возможности использовать синтаксический сахар C#, такой как yield
а также foreach
,
Я с нетерпением жду ваших рекомендаций.
2 ответа
Если ваше перечисление никогда не перестанет давать результаты, было бы лучше потребовать, чтобы разработчик четко указывал, какую максимальную сумму следует генерировать, чтобы вы исключили риск возникновения бесконечного цикла, когда разработчик использует for each
Синтаксис с вашим перечислимым. например
IEnumerable<T> ProvideCopies<T>(T original, int howMany) where T: IMyCloneable<T>
{
return Enumerable.Range(1, howMany).Select(x => original.Clone());
}
или, если вы старая школа:
IEnumerable<T> ProvideCopies<T>(T original, int howMany) where T: IMyCloneable<T>
{
for (var i = 0; i < howMany; i++)
yield return original.Clone();
}
Таким образом, разработчик не обязан использовать все перечисление и может остановиться в любое время (т.е. вы все равно уступаете по ходу дела), но вы знаете, что он остановится в какой-то момент, вместо того, чтобы вводить возможность блокировки вашего кода навсегда в производстве из-за ошибки в коде.
Если вы действительно не можете сказать, сколько копий потребуется, и вам нужно продолжать генерировать копии, пока что-то еще не скажет вам остановиться, тогда IObservable<T>
будет лучше подходить для этого сценария, так как он предназначен для "прослушивания" вечно, пока вы Dispose()
этого
Да, я думаю, что это действительно плохая практика. Поток не сможет обрабатывать любые другие пути кода. И это может создать большую нагрузку на процессор.