序列不包含任何元素。

5
有人能解释一下为什么以下的LINQ查询会抛出InvalidOperationException异常吗?(不要说列表没有元素,我要查找的值总是存在于集合中)
class Program
{
     static int lastNumber;
     static void Main()
     {
          int loopCount = 100; int minValue = 1; int maxValue = 10000;
          var numbers = Enumerable.Range(minValue, maxValue).ToList();//or ToArray();
          Random random = new Random();

          for (int i = 0; i < loopCount; i++)
          {
              //.First() throws the exception but it is obvious that the value exists in the list
              int x = numbers.Where(v => v == NewMethod(minValue, maxValue, random)).First();
          }
          Console.WriteLine("Finished");
          Console.ReadLine();

     }

     private static int NewMethod(int minValue, int maxValue, Random random)
     {
         var a1 = random.Next(minValue + 1, maxValue - 1);
         lastNumber = a1;
         return a1;
     }
}

只有在我在lambda表达式中调用NewMethod时才会出现问题。
如果这样做,它就能正常工作。

int temp=NewMethod(minValue, maxValue, random);
int x = numbers.Where(v => v == temp).First();

我在代码中添加了lastNumber字段来帮助调试,当程序崩溃时,您可以看到该值存在于集合中。
PS 问题不在于随机变量,我已经删掉了参数并在方法内部创建了一个新的局部随机数,但问题仍然存在。
更新 事实证明,您不需要循环就能使其崩溃。如果您多次运行程序,将再次出现错误。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
class Program
{
    static int lastNumber;
    static void Main()
    {
        int minValue = 1, maxValue = 100000;
        var numbers = Enumerable.Range(minValue, maxValue).ToArray();
        //Crashes sometimes
        int x = numbers.Where(v => v == NewMethod(minValue, maxValue)).First();
        Console.WriteLine("Finished");
        Console.ReadLine();
     }

     private static int NewMethod(int minValue, int maxValue)
     {
          Random random = new Random();
          var a1 = random.Next(minValue + 1, maxValue - 1);
          lastNumber = a1;
          return a1;
      }
 }

据我所知,您不能在lambda表达式中使用“复杂”的方法,只能使用可以转换为语句的内容。 - Ziv Weissman
1
@ZivWeissman 你可以调用“复杂”的方法 - 那个问题是在谈论带有副作用的方法。 - D Stanley
2个回答

5
@Oleg 是正确的,但这里是问题所在:Where扫描列表,寻找符合给定条件的元素。在这种情况下,每个元素的条件都不同。如果将问题缩小,比如一个由5个元素组成的数组:
List<Int32> (5 items)
1 
2 
3 
4 
5 

然后循环遍历查找与某个随机数(r)匹配的值(Item[i]为当前值):

Item 1: x = 1, r = 2  // fail
Item 2: x = 2, r = 3  // fail
Item 3: x = 3, r = 2  // fail
Item 4: x = 4, r = 3  // fail
Item 5: x = 5, r = 2  // fail

请注意,没有任何项目与该特定随机数匹配,因此没有项目符合条件,First会抛出异常!
正如你已经发现的那样,修复方法是在枚举之前生成随机数:

int temp=NewMethod(minValue, maxValue, random);  // say 2

Item 1: x = 1, temp = 2  // fail
Item 2: x = 2, temp = 2  // success!

顺便提一下:

在这里使用maxValue有点误导人:

 Enumerable.Range(minValue, maxValue)

由于Enumerable.Range的第二个参数是结果集合的长度而不是最大值,因此需要注意。在这种情况下,由于您从1开始,所以它可以正常工作,但如果您使用99和100,则会得到从99到198的范围,结果将出乎意料。


@GeorgeVovos 不要难过 - 我自己也不得不模拟一下才看出问题! - D Stanley

4

这是因为NewMethod在每次迭代中被调用,每次都会生成新的随机数。

但是在这段代码中,首先生成数字,然后再将其与numbers集合中的每个元素进行比较。

int temp=NewMethod(minValue, maxValue, random);
int x = numbers.Where(v => v == temp).First();

1
我不确定我理解了,新生成的数字始终存在于集合中。为什么会崩溃? - George Vovos
这是真的,数字始终存在于整个集合中,但是这个数字与当前用于测试相等性的数字不相等。 - Oleg
哦,我明白你的意思了。NewMethod 在很多地方被调用,当你说迭代时,我以为是 for 循环。 - George Vovos

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