我经常在我们的项目中遇到非信息性消息,例如"序列包含多个元素"。它不时发生。
最好不要检查:
var count = collection.Count(i => predicate);
if (count > 1)
{
throw new InformativeException("...");
}
if (count == 0)
{
throw new AnotherInformativeException("...");
}
我真的不明白在什么情况下Single()/ SingleOrDefault()可能有用。只有当你捕获InvalidOperationException并尝试将其转换为更具信息量的时候。
答案 0 :(得分:2)
您是对的,错误消息是通用的,不是非常具体。但是,我认为他们不需要:
有两种情况:
合理地可能存在<> 1
个元素(例如,由于配置文件条目无效或用户输入错误)。在这种情况下,检查列表中的项目数量并抛出信息性异常是正确的做法。
有<> 1
个元素是不可能的 - 也就是说,如果你在这里得到<> 1
个元素,那么你在其他地方就会出现编程错误。例如:
// getItem's documentation guarantees that each element has a unique ID and
// that there is an element with ID 1.
var myItems = getItems();
// if this fails, there is an implementation error in getItems
var item1 = myItems.Single(x => x.ID == 1);
在这种情况下,默认错误消息通常就足够了。在这里使用Single
基本上充当运行时断言。
答案 1 :(得分:1)
Single和SingleOrDefault()可用于以下场景:
Single()期望在您正在使用的匹配序列中找到单个元素。 如果序列为空或包含多个元素,它会抛出一个预期。
SingleOrDefault()在匹配序列为空(并返回详细信息,通常为null
)时不会抛出异常,但在匹配序列包含多个元素时会抛出异常。
您还可以使用以下LINQ函数:
答案 2 :(得分:0)
Single
和SingleOrDefault
。
你可以通过明确地检查计数(这是传统的方法)来做你建议的方式,但是当你让LINQ为你抛出异常时,代码可以更简洁和/或更容易理解:
try {
// a bunch of stuff
var x = y.Where(z => z.ID = id).Single();
// more stuff
} catch (SomeException e) {
// other expected issue
} catch (OtherException e) {
// other expected issue
} catch (InvalidOperationException e) {
// how'd we wind up with more (or less) than 1???
} catch (Exception e) {
// unexpected issue
}
有些人(包括我自己)可能更喜欢将主要逻辑块的外围验证代码(基本上就是上面所代表的代码)放在一边。请注意,这并没有忽略主要逻辑要求,因为Single()
明确地告诉我们,我们期望只有一个项目,因此通过将计数验证移动到异常处理程序不会丢失知识。
我确实希望它抛出一个比InvalidOperationException
更具体的例外但是......这非常通用。
答案 3 :(得分:0)
如果我希望正好一个元素,我会使用以下模式:
try {
var singleElement = x.SingleOrDefault(x => /* some expression */);
}
catch (InvalidOperationException iopex) {
throw new InformativeException("More than one element found.");
}
if (singleElement == null) {
throw new InformativeException("No element found.");
}
// work with singleElement from here