C# 当找到多个元素时,为什么Single()不直接返回?
在使用反编译器检查该方法时,我(大致)在该方法中发现了以下代码:C# 当找到多个元素时,为什么Single()不直接返回?,c#,.net,linq,internals,C#,.net,Linq,Internals,在使用反编译器检查该方法时,我(大致)在该方法中发现了以下代码: foreach (TSource current in source) { if (predicate(current)) { result = current; num += 1L; } } if (num > 1L) { throw Error.MoreThanOneMatch(); } 如您所见,它在投掷之前会在所有项目上循环。当num>1时,它为什
foreach (TSource current in source)
{
if (predicate(current))
{
result = current;
num += 1L;
}
}
if (num > 1L)
{
throw Error.MoreThanOneMatch();
}
如您所见,它在投掷之前会在所有项目上循环。当
num>1
时,它为什么不中断?同意,从性能角度来看,它会更好(编辑:如果我们希望有多个项目与谓词匹配,我们不应该这样做):
看起来他们决定让分析结果更加清晰,并将其与枚举源分离。但我想知道为什么不使用简单开关:
switch((int)num)
{
case 0: throw Error.NoMatch();
case 1: return local;
default:
throw Error.MoreThanOneMatch();
}
关于性能问题——我认为假设在您真正期望单个结果时应该调用
Single
。零个或多个结果是异常路径,不应经常发生(与任何异常一样)。因此,如果源包含许多匹配谓词的项,则程序的逻辑错误更大。BySingle
的意思是,正好是一个,而不是一个,也不是多个它枚举所有项目,以确保只有一项。
如果没有异常或存在多个异常,则会引发异常。
SingleOrDefault
如果有更多,则抛出,如果没有,则返回default(T)
/null
您要查找的是FirstOrDefault
,如果它找到与谓词匹配的第一个枚举,它将破坏枚举First
如果没有则抛出,如果找到了第一个,则还会中断(直接从返回)它的foreach
FirstOrDefault的来源
foreach (TSource current in source)
{
if (predicate(current))
{
return current;
}
}
return default(TSource);
而First的源不是返回默认值
throw Error.NoMatch();
谢谢你的提问。LINQ团队决定在这里循环到底,这真的很有趣。文斯,这部分的可枚举单实现和可查询单实现之间有什么区别吗?你在哪里找到这个实现的?使用Reflector,我看到了完全不同的东西,如果找到第二个元素,它会立即返回。从性能上看,这可能并不完美。但至少结果是可以预测的。谓词是为任何元素执行的,因此,如果谓词有副作用,那么它对所有元素都是可预测的。如果在第二次命中时出现单断点,则很难判断谓词执行了哪些元素。所以我想说,在某些情况下这更安全,但在大多数情况下性能较差。@Thomaslevsque 3.5汇编或4.0?我想您的交换机解决方案需要枚举整个源代码。但是当前带有if的解决方案在知道存在一些重复性时会立即抛出异常。@Ondra当前解决方案不会立即抛出异常。看看源代码。我的开关块是一个重构的
num
分析块,来自原始实现。这个开关应该在枚举之后放置这是我最初的想法,但问题是,如果找到两个匹配的元素,为什么没有在该点抛出异常,而不是遍历列表的其余部分然后抛出异常?对不起,我的错误,SingleOrDefault
从不抛出,如果有多个或没有,则返回null<代码>单个是抛出的。我的猜测是:他们希望迭代在抛出异常之前完成。如果它们在枚举中间抛出一个异常,它可能会引起一些副作用(如枚举器)。接下来从Internet下载一个项目,除此之外,连接不正确。closed@Ondra是的,这是一个老问题,但不是,人们永远不应该相信枚举器/产量方法已经“完成”(关于实现IEnumerator的Reset方法,经常提到没有人指定必须触发,foreach也没有;yield方法也不应该处理打开/关闭db连接,因为它们在“结束”之前不一定执行)SingleOrDefault
在有多个项时会抛出异常,就像Single
一样。区别在于没有项时:Single
抛出,SingleOrDefault
返回默认值(T)
(对于引用类型为空)
throw Error.NoMatch();