为什么在c#中使用"yield"关键字进行延迟执行会有不同的运行时行为?

Raf*_*het 1 .net c# yield keyword deferred-execution

如果IgnoreNullItems在下面的sammple代码中调用扩展方法,则延迟执行按预期工作,但是在使用时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?
Run Code Online (Sandbox Code Playgroud)

感谢您分享您的想法!

Raffael Zaghet

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个具有相同行为的版本:

这是一个显示相同行为的版本.在这种情况下,不要让resharper"改进"你的foreach语句;) - > resharper使用return语句将foreach更改为"IgnoreNullItemsHavingDifferentBehaviour"版本.

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;
            }
Run Code Online (Sandbox Code Playgroud)

Chr*_*ney 5

由于IgnoreNullItemsHavingDifferentBehaviour本身不包含任何"yield",因此会立即引发异常.

相反,它是IgnoreNulls,它被转换为迭代器块,因此使用延迟执行.

这实际上是Jon Skeet在他的EduLinq系列中用来强制立即对源序列进行空检查的方法.有关更详细的说明,请参阅此文章(特别是"让我们实现它"部分).