Поддерживает ли неуниверсальная версия IEnumerable отложенное выполнение?

Если да, то в каких версиях .NET Framework он поддерживается?

Я протестировал это на .NET Framework 4.0, и он отлично работает:

using System;
using System.Collections.Generic;

public class TestClass
{
    public IEnumerable Defer()
    {
        yield return 1;
        yield return 2;
        yield return 3;
    }
}

person Edgar Gonzalez    schedule 07.01.2011    source источник
comment
Итераторы (ключевое слово yield) впервые стали доступны в C# версии 2, поставляемой с VS2005.   -  person Hans Passant    schedule 07.01.2011


Ответы (3)


Да, он поддерживается с тех пор, как было ключевое слово yield. Единственная разница в том, что это более или менее IEnumerable<object>, что может привести к неэффективности, если он должен делать бокс. Кроме этого, это точно так же.

person Mike Caron    schedule 07.01.2011
comment
Отложенное выполнение не имеет ничего общего с yield. yield — это просто синтаксический сахар. Нет никаких причин, по которым вы не можете отложить выполнение в .NET 1.0, используя IEnumerable. - person leppie; 07.01.2011
comment
Истинный. Но в примере ОП специально используется yield, что указывает на то, что он не нацелен на 1.0. (Кто-нибудь в эти дни?) - person Mike Caron; 08.01.2011
comment
Спасибо, что напомнили мне, что yield — это просто синтаксический сахар. Насчет использования 1.0 согласен - person Edgar Gonzalez; 08.01.2011

Поскольку ключевые слова yield сведены к обману компилятора, по-видимому, это должно работать. Это определенно работает для среды выполнения 2.0; Однако я бы не стал делать какие-либо заявления о 1.1.

person Ben    schedule 07.01.2011

Неуниверсальный IEnumerable не реализует IDisposable. Может случиться так, что VB.Net и C# будут использовать утиный тип либо IDisposable, либо метода .Dispose() при использовании перечислителя, который не поддерживает IEnumerable(Of T), но, безусловно, нельзя полагаться на всех потребителей неуниверсального IEnumerable для этого. Если потребитель перечислимого не выполняет .Dispose() должным образом, выполнение перечислителя, включая явные или неявные операторы finally, будет прекращено.

person supercat    schedule 12.01.2011
comment
Я ищу дополнительную информацию об этом; Я просто не могу понять этот очевидный вопиющий недостаток дизайна с IEnumerable. Я читал эту статью, codeproject. com/Articles/29534/, в котором в разделе «Обратная совместимость» указано, что клиентский код, ожидающий неуниверсальных IEnumerator, неправильно удаляет итератор. К какой ошибке это может привести? - person TamaMcGlinn; 20.03.2018
comment
@TamaMcGlinn: рассмотрим случай коллекции, которая используется в нескольких потоках. Если весь код, который перечисляет коллекцию, вызывает GetEnumerator, быстро перечисляет коллекцию, а затем вызывает Dispose для этого перечислителя, то GetEnumerator получает блокировку, а Dispose освобождает, это может работать хорошо. Однако, если Dispose никогда не вызывается, код, требующий использования коллекции, может быть заблокирован на неопределенный срок. Возможно, можно обойти это, если коллекция будет содержать ссылку, которая будет идентифицировать активный перечислитель (если он существует), и иметь код, который... - person supercat; 21.03.2018
comment
... проверьте, удерживается ли блокировка перечислителем, и - если да - начните с небольшого ожидания блокировки. Если это не удается, код может затем задействовать вторичную блокировку [которую перечислитель должен будет получать и освобождать каждый раз, когда он фактически получает элемент], перечислять оставшуюся часть коллекции в список, сообщать перечислителю, чтобы он извлекал оставшиеся данные из этого список, а затем украсть основной замок. Однако это добавило бы много сложности, в которой не было бы необходимости, если бы пользователи счетчика придерживались надлежащей дисциплины. - person supercat; 21.03.2018
comment
Понял. Значит ли это, что наследование IEnumerator‹T› от IEnumerator нарушает принцип подстановки Лискова? Вы не можете просто передать кому-то IEnumerator‹T›, когда они указывают в интерфейсе, что они принимают все IEnumerator, потому что тогда такие интерфейсы не будут удалять IEnumerator‹T›, что может вызвать проблему блокировки, как вы описали . - person TamaMcGlinn; 26.03.2018
comment
@TamaMcGlinn: Проблема в основном в том, что IEnumerable.GetEnumerator может возвращать любую из трех вещей: объект, от которого можно безопасно отказаться, но не реализует IDisposable, объект, от которого можно отказаться, только вызвав Dispose, или объект, от которого можно безопасно отказаться с вызовом Dispose или без него, и у клиентского кода нет хорошего способа обрабатывать все три. IEnumerable<T> гарантирует, что он не вернет объекты первого типа. Первоначальные разработчики IEnumerable, возможно, ожидали, что он никогда не вернет объекты второго типа, но это никогда не было реалистичным. - person supercat; 26.03.2018
comment
@TamaMcGlinn: ИМХО, настоящая проблема заключается в том, что и IEnumerable, и IEnuerable<T>, а также IEnumerator и IEnumerator<T> опускают функции и свойства, которые должны поддерживать все реализации, например. выдаст ли вызов GetEnumerator объект, от которого можно безопасно отказаться, знаете ли вы, сколько предметов вы содержите, или вы всегда будете сообщать об одном и том же содержимом. Это должно быть обработано с помощью свойства, а не использования различных интерфейсов, чтобы позволить перечислителям быть обернутыми другими перечислителями без взрывного роста типов оболочек. - person supercat; 26.03.2018