【发布时间】:2017-10-26 13:31:38
【问题描述】:
我继承了一个方法,它返回一个 IEnumerable<whatever>,并通过 yield return myWhatever 构造实现:
public IEnumberable<whatever> GetWhatevers() {
while (true) {
// do calculations
yield return myWhatever
}
}
我发现了一个错误,如果该类的集合之一为空,它会导致无限循环,因此我添加了一个检查并编写了一个测试:
public IEnumberable<whatever> GetWhatevers() {
if (_dependentList == null || _dependentList.Count == 0) {
throw new InvalidOperationException("Unable to process without whatevers");
}
while (true) {
// do calculations
yield return myWhatever
}
}
void MyWhateverThrowsIOEOnEmptyList() {
var sut = new MyThing(null);
Assert.ThrowsException<InvalidOperationException>(() => {
var results = sut.GetWhatevers();
});
}
我现在意识到这个测试失败了,因为即使我调用了该方法(由于 yield 构造),我也从未真正开始迭代结果。
我可以通过这样做来修复测试:
void MyWhateverThrowsIOEOnEmptyList() {
var sut = new MyThing(null);
Assert.ThrowsException<InvalidOperationException>(() => {
var results = sut.GetWhatevers().ToList();
});
}
...但我不喜欢它。这意味着无论调用者调用此方法,在尝试访问结果之前,他们都不知道其中隐藏了一个 IOE。这个结果变量可能会往返于 Timbucktu,但始终不知道它是一个定时炸弹。
所以现在,每当我使用这种方法时,我必须记住其中可能潜伏着一个异常。
这个方法太复杂了,我不想用传统的循环重写。有没有一种方法可以强制在“调用时”而不是在“解决时”抛出异常,以便在调用时知道存在问题?
【问题讨论】:
-
(这个丑陋的小副作用似乎不值得
yield...) -
您可以将
yield return代码放入private GetWhateversInternal()并让公众GetWhatevers()进行检查,然后return GetWhateversInternal();。 -
迭代器只有一件有用的事情。他们会迭代它,保证。无论你使用 ToList() 还是 foreach 或调用 MoveNext() 都无所谓,都一样。
-
@HansPassant - 我没有意识到这本身就是一个迭代器(第一次处理
yield),但我认为我们即将对我们的命名约定进行微调!
标签: c# exception yield-return