2015-06-18 17 views
5

Qualcuno può spiegarmi perché la seguente query LINQ genera una InvalidOperationException?
(Non dire che la lista non ha elementi, il valore che sto cercando sempre esiste nella collezione)La sequenza non contiene elementi

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; 
    } 
} 

Il problema appare solo quando chiamo newMethod dentro la mia lambda expession.
Se fare questo funziona

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

ho aggiunto il campo lastNumber per aiutare il debug del codice, si può vedere che il valore esiste nella collezione quando si blocca

PS
Il problema non è il variabile casuale, ho rimosso il parametro e creare un nuovo locale casuale all'interno del metodo, ma il problema esiste ancora

Aggiorna

risulta fuori che non è necessario il ciclo per farlo crash. Se si esegue il programma molte volte si otterrà l'errore di nuovo

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; 
     } 
} 
+0

Per quanto ne so non puoi utilizzare un metodo "complesso" all'interno dell'espressione lamda, solo le cose che possono essere convertite in istruzioni. http://stackoverflow.com/questions/1883920/call-a-function-for-each-value-in-a-generic-c-sharp-collezione –

+1

@ZivWeissman È possibile chiamare i metodi "complessi" - quella domanda era parlando di metodi _con effetti collaterali_. –

risposta

5

@Oleg è giusto, ma qui è il motivo per cui è il problema.

Where esegue la scansione dell'elenco alla ricerca di elementi corrispondenti ai criteri specificati. In questo caso i criteri cambiano per ogni elemento. Se hai fatto il problema più piccolo, dire una serie di 5 elementi:

List<Int32> (5 items) 
1 
2 
3 
4 
5 

E poi loop-through alla ricerca di un valore che corrisponde ad alcune di numeri casuali (x è Item[i] e r è un numero casuale):

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 

Si noti che nessun elemento corrisponde a quel particolare numero casuale, quindi nessun elemento corrisponde ai criteri e First genera un'eccezione!

La correzione, come avete scoperto, è quello di generare il numero casuale prima l'enumerazione:

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

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

Nota a margine:

E 'un po' fuorviante per usare maxValue qui:

Enumerable.Range(minValue, maxValue) 

Poiché il secondo parametro su Enumerable.Range è la lunghezza della raccolta risultante, non il valore massimo.In questo caso funziona perché stai iniziando a 1 ma i risultati sarebbero inattesi se usassi, diciamo 99 e 100 - otterresti un intervallo da 99 a 198.

+0

Non posso credere di essere stato così stupido !!! Penso di dover andare a dormire !!! –

+0

@GeorgeVovos Non mi sento male - ho dovuto simularlo da solo per vedere il problema! –

4

È perché newMethod invoca ad ogni iterazione e ogni volta che genera il nuovo numero casuale.

Ma in questo codice inizialmente genera il numero e quindi si confronta con ciascun elemento della raccolta dei numeri.

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

Non sono sicuro di aver capito, il nuovo numero generato esiste sempre nella collezione. Perché si blocca? –

+0

È vero che il numero sempre presente in tutta la raccolta, ma questo numero non è uguale al numero corrente su cui si basa l'uguaglianza dei test. – Oleg

+0

ooo, capisco cosa intendi, NewMethod è invocato dentro dove molte volte, quando hai detto l'iterazione ho pensato al ciclo for –

Problemi correlati