除非我使用ToList(),否则序列上的扩展方法不会抛出异常

时间:2017-06-09 19:57:11

标签: c# linq nunit

我正在研究扩展方法,我构建了一个适用于序列部分的方法。如果该部分的传递维度不是严格肯定的,那么它应该抛出ArgumentOutOfRangeException,但是在我的nunit测试中,我设法让它只通过调用函数结果上的ToList()来抛出异常。 这是我的代码:

    public static class ExClass {
        public static IEnumerable<T> SplitAndReverse<T>(this IEnumerable<T> sequence, int size) {
            // Check if sequence is null
            if (size <= 0)
               throw new ArgumentOutOfRangeException("size can only be a positive number");
            // Do something
            }
        }
    }

    [TestFixture]
    public class TestClass {

        [TestCase(0)]
        public void Test2(int range)
        {
            Assert.That(() => Enumerable.Range(1,10).SplitAndReverse(range).ToList(), Throws.TypeOf<ArgumentOutOfRangeException>());
        }
    }

我还尝试删除ToList()方法并将结果保存在var中。该变量中的值位于此屏幕截图中,但我不能很好地理解它。在最后一行中,似乎是Item抛出了ArgumentOutOfRangeException(为什么以及为什么要使用Items?)。在第二行中,它告诉“ArgumentOutOfRangeException”类型不是有效类型(再次,为什么?)。

While debugging the result of the method

可能有助于了解的最后一件事是,如果没有ToList()方法,我无法在调试模式下输入该方法,同时输入没有问题。我有另一个测试只传递函数的有效参数,从该测试我可以设法进入该方法(它也按预期工作)。

2 个答案:

答案 0 :(得分:3)

Enumerable.Range(1,10).SplitAndReverse(range)只是一个查询而没有.ToList()没有任何内容可以运行查询评估(这就是为什么调试器不起作用,因为没有ToList()SplitAndReverse没有执行)。 LINQ查询是惰性求值的,仅在您使用foreach枚举它们时执行,或者您调用其中一种具体化方法,例如ToList()ToArray()Count(),{{ 1}}。

答案 1 :(得分:3)

返回IEnumerable的所有内容都是惰性评估的,这是不正确的。考虑这种方法:

public static IEnumerable<T> SplitAndReverse<T>(this IEnumerable<T> sequence, int size)
{
    // Check if sequence is null
    if (size <= 0)
        throw new ArgumentOutOfRangeException("size can only be a positive number");
    // just return
    return sequence;            
}

它只返回相同的序列,没有yield return语句。现在,如果你这样做:

Enumerable.Range(1,10).SplitAndReverse(-1)

没有ToList - 它将进入SplitAndReverse并将抛出异常。为什么不呢?这只是一种常规方法。

现在当你使用yield return时 - 你的方法被编译器转换为不同的东西(状态机),当你调用这样的方法时 - 它的主体将不会被立即执行(延迟评估)。相反 - 只有在枚举结果时才会执行(部分)正文(例如通过调用ToList,就像你的情况一样)。大多数默认的LINQ方法(WhereSelect等)确实使用了这种惰性求值,但您自己的方法可能会也可能不会这样做。