为什么我得到“序列不包含元素”?

时间:2012-11-14 17:37:49

标签: c# linq

注意:请参阅底部的修改。我是个白痴。

拥有以下代码来处理标记名称集并识别/处理新标记:

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运行(并调用MoveNext)newTagNames时,抛出异常。例外情况说:

  

序列没有元素

我对此感到困惑。为什么foreach会坚持序列非空?我打算在调用First()时看到错误,但在使用foreach时却没有?


编辑:更多信息。

这一刻变得更加怪异。因为我的代码是异步方法,而且我很迷信,所以我认为引发异常的点与它被捕获和报告的点之间存在太大的“距离”。所以,我在一个有问题的代码周围进行了try / catch,希望能够验证被抛出的异常真的是我认为的那样。

所以现在我可以在调试器中逐步进入foreach行,我可以验证序列是空的,并且我可以直接到调试器突出显示单词“in”的位。还有一步,我在我的异常处理程序中。

但是,不是我刚添加的异常处理程序,不是!它落在我最外层的异常处理程序中,而不访问我最近添加的异常处理程序!它与catch (Exception ex)不匹配,也与普通catch不匹配。 (我也确实放了一个finally,并确认它确实在出路时访问了它。

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


编辑2

好的,嗯,误报......我的本地try / catch没有抓住异常只是因为它没有被我想到的代码引发。正如我上面所说,我看到调试器中的执行从foreach的“in”跳转到外部异常处理程序,因此我(错误的)假设这就是错误所在。但是,使用空枚举,这只是函数中执行的最后一个语句,并且由于某种原因,调试器没有向我显示函数的跳出或在调用点执行下一个语句 - 这是在事实上导致错误的那个。

向所有回复的人道歉,如果你想创造一个回答说我是一个偶像,我会很乐意接受它。也就是说,如果我再次露出我的脸......

2 个答案:

答案 0 :(得分:0)

它不是那么合适或干净,但这是如何工作的:

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

答案 1 :(得分: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之前得到异常。