我正在研究扩展方法,我构建了一个适用于序列部分的方法。如果该部分的传递维度不是严格肯定的,那么它应该抛出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”类型不是有效类型(再次,为什么?)。
可能有助于了解的最后一件事是,如果没有ToList()方法,我无法在调试模式下输入该方法,同时输入没有问题。我有另一个测试只传递函数的有效参数,从该测试我可以设法进入该方法(它也按预期工作)。
答案 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方法(Where
,Select
等)确实使用了这种惰性求值,但您自己的方法可能会也可能不会这样做。