1

如果您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 使用 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;
            }
4

3 回答 3

5

立即引发异常,因为IgnoreNullItemsHavingDifferentBehaviour本身不包含任何“yield”。

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

这实际上是 Jon Skeet 在他的 EduLinq 系列中用来强制对源序列立即进行空值检查的方法。请参阅这篇文章以获得更详细的解释(特别是“让我们实现它”部分)。

于 2011-04-28T11:19:00.487 回答
4

我没有测试过,但我猜...

使用该IgnoreNullItems方法,整个方法将延迟到您成为枚举为止。使用您的替代方法,仅IgnoreNulls延迟执行 - 空值签入IgnoreNullItemsHavingDifferentBehaviour立即发生。

于 2011-04-28T11:11:18.320 回答
4

延迟执行来自如何yield return工作。它将在一个方法内创建状态机,在您尝试枚举第一项之前,该方法不会启动或执行任何代码。

但是当没有yield return它时,它会表现得像普通方法一样。

在 Jon Skeet 的Edulinq中得到了完美的解释和展示。

于 2011-04-28T11:20:32.980 回答