为什么我会收到“Sequence contains no elements”错误信息?

5

注意:请参考底部的编辑内容。我是个白痴。

我有以下代码来处理一组标签名称并识别/处理新的标签:

IEnumberable<string> tagNames = GetTagNames();
List<Tag> allTags = GetAllTags();

var newTagNames = tagNames.Where(n => !allTags.Any(t => t.Name == n));

foreach (var tagName in newTagNames)
{
    // ...
}

这个方法一直很好用,但当遇到带有 "Foo" 标签的情况且列表中只包含 "foo" 时,则会出现错误。换句话说,它没有进行大小写不敏感比较。

我更改了测试方法,使用了大小写不敏感比较,如下所示:

var newTagNames = tagNames.Where(n => !allTags.Any(t => t.Name.Equals(n, StringComparison.InvariantCultureIgnoreCase)));

当我运行foreach(并在newTagNames上调用MoveNext)时,突然抛出了异常。 异常显示:

序列中没有元素

我对此感到困惑。为什么foreach坚持要求序列非空?如果我调用First(),我会期望看到这个错误,但不是使用foreach吧?


编辑:更多信息。

这变得越来越奇怪。因为我的代码在异步方法中,并且我很迷信,所以我决定在有问题的代码周围放置try/catch,希望验证引发异常的确是我认为的那样。

现在我可以在调试器中逐步执行foreach行,我可以验证序列为空,我可以一直走到调试器突出显示单词“in”的位置。再走一步,就进入了我的异常处理程序。

但是,它没有进入我刚添加的异常处理程序!它没有匹配catch (Exception ex),也没有匹配普通的catch。(我还加了一个finally,并验证了它在退出时访问)。

我始终认为这样的异常处理程序会捕获任何异常。我现在很害怕。我需要一个成年人。


编辑2

好的,嗯,虚惊一场...只是因为我的本地try/catch没有捕获到异常,这个异常并不是由我认为的代码引发的。正如我上面所说,我在调试器中观察执行过程,从foreach的“in”直接跳转到外部异常处理程序,因此我(错误地)假设那是错误的位置。然而,在空枚举中,那只是函数内执行的最后一个语句,由于某种原因,调试器没有显示我在调用点的函数之外的步骤或下一个语句的执行 - 实际上是导致错误的语句。

对于所有回复的人,我表示歉意,如果您想创建一个答案,说我是个白痴,我会非常感激。也就是说,如果我再次出现在SO上...


4
能否提供一个简短而完整的程序来展示问题? - Jon Skeet
2
你确定这个错误不是来自于 GetTagNames() 吗?尝试在它后面添加 .ToList() 进行测试。 - Magnus
我不确定是什么导致了那个错误。但我想你想要使用 String.Compare()Equals 方法比较的是引用类型,看它们是否引用同一个对象,这不是你在这里想要的。 - Jonathan Wood
1
@JonathanWood 不是这样的。它重载了 Equals 方法来比较字符串的基础值。它首先进行引用比较作为优化,可能会短路(所有的 Equals 都可以选择性地执行此操作),但这并不是真正相关的。 - Servy
1
如果你遇到了一个无法捕获的异常,那就非常奇怪了...在我亲眼看到它之前,我需要确认这确实发生了,而不仅仅是你诊断工具的问题。如果你能够重现这个问题,那将会非常有趣。 - Jon Skeet
显示剩余7条评论
2个回答

0

这不是很规范或干净,但是这个怎么工作:

var newTagNames = tagNames.Where(n => !allTags.Any(t => t.Name.ToUpper() == n.ToUpper()));

我下投票了,因为您提供了一种不同的比较字符串的建议,这根本没有解决问题。 - Amy B
1
@DavidB 公平地说,他确实说了当他改用字符串比较后问题开始出现。 - Magnus

0

在延迟操作中处理异常很有趣。Enumerable.Where方法(以及大多数linq方法)直到查询被枚举才会执行。

IEnumerable<int> query = Enumerable.Empty<int>();
int myNum = 0;
try
{
  query = Enumerable.Range(1, 100).Where(i => (i/myNum) > 1);
}
catch
{
  Console.WriteLine("I caught divide by zero"); //does not run
}

foreach(int i in query) //divide by zero exception thrown
{
  //..
}

在您的特定情况下:
IEnumberable<string> tagNames = GetTagNames();

我敢打赌 GetTagNames 里面有一个 Enumerable.First 或者 Enumerable.Single。如果 GetTagNames 的结果是延迟的,你必须枚举该结果才能使异常发生。这就是为什么评论者建议您在 GetTagNames 的结果上调用 ToList - 以枚举它并在复杂查询中使用 tagNames 之前使异常发生。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接