Почему существует другое поведение во время выполнения с отложенным выполнением с использованием ключевого слова yield в c #? - PullRequest
1 голос
/ 28 апреля 2011

Если вы вызываете метод расширения IgnoreNullItems в коде выборки ниже, отложенное выполнение работает, как и ожидалось, однако при использовании IgnoreNullItemsHavingDifferentBehaviour исключение возникает немедленно.Почему?

List<string> testList = null;
testList.IgnoreNullItems(); //nothing happens as expected

testList.IgnoreNullItems().FirstOrDefault();
//raises ArgumentNullException as expected

testList.IgnoreNullItemsHavingDifferentBehaviour(); 
//raises ArgumentNullException immediately. not expected behaviour -> 
//  why is deferred execution not working here?

Спасибо, что поделились своими идеями!

Раффаэль Загет

public static class EnumerableOfTExtension
{
    public static IEnumerable<T> IgnoreNullItems<T>(this IEnumerable<T> source)
        where T: class
    {
        if (source == null) throw new ArgumentNullException("source");

        foreach (var item in source)
        {
            if (item != null)
            {
                yield return item;
            }
        }
        yield break;
    }

    public static IEnumerable<T> IgnoreNullItemsHavingDifferentBehaviour<T>(
        this IEnumerable<T> source) 
        where T : class
    {
        if (source == null) throw new ArgumentNullException("source");

        return IgnoreNulls(source);
    }

    private static IEnumerable<T> IgnoreNulls<T>(IEnumerable<T> source)
        where T : class
    {
        foreach (var item in source)
        {
            if (item != null)
            {
                yield return item;
            }
        }
        yield break;
    }
}

Вот версия с таким же поведением:

Здесьверсия, которая показывает то же поведение.В этом случае не позволяйте resharper «улучшать» оператор foreach;) -> resharper меняет foreach на версию «IgnoreNullItemsHavingDifferentBehaviour» с оператором return.

public static IEnumerable<T> IgnoreNullItemsHavingSameBehaviour<T>(this IEnumerable<T> source) where T : class
            {
                if (source == null) throw new ArgumentNullException("source");

                foreach (var item in IgnoreNulls(source))
                {
                    yield return item;
                }
                yield break;
            }

Ответы [ 3 ]

5 голосов
/ 28 апреля 2011

Исключение возникает немедленно, потому что IgnoreNullItemsHavingDifferentBehaviour не содержит сам "yield".

Скорее, это IgnoreNulls , которое преобразуется в блок итератора итаким образом, используется отложенное выполнение.

Это на самом деле подход, который Джон Скит использовал в своей серии EduLinq для принудительной немедленной проверки нуля исходных последовательностей.См. этот пост для более подробного объяснения (в частности, раздел «Давайте реализуем это»).

4 голосов
/ 28 апреля 2011

Отсроченное выполнение зависит от того, как работает yield return.Он создаст конечный автомат внутри метода, который не будет запускаться или выполнять какой-либо код, пока вы не попытаетесь перечислить первый элемент.

Но при отсутствии yield return он будет вести себя как обычный метод.

Прекрасно объяснено и показано в книге Джона Скита Edulinq .

4 голосов
/ 28 апреля 2011

Я не проверял, но могу догадаться ...

При использовании метода IgnoreNullItems весь метод откладывается до тех пор, пока вы не будете перечислением.При использовании альтернативного метода откладывается только выполнение IgnoreNulls - проверка на ноль в IgnoreNullItemsHavingDifferentBehaviour происходит немедленно.

...