2009-06-04 18 views
19

Immaginate il seguente codice:Foreach può lanciare InvalidCastException?

class foreach_convert 
{ 
    public static void method2() 
    { 
     List<IComparable> x = new List<IComparable>(); 
     x.Add(5); 

     foreach (string s in x) 
     { 
      //InvalidCastException in runtime 
     } 
    } 
} 

mi chiedo, perché è questo comportamento foreach così ... non C# -come? Quello che succede qui è un cast implicito in una sottoclasse, che è soggetta a errori e sembra essere vietata in ogni altro posto nella lingua. O non ho ragione?

P.S. La ragione per cui sto chiedendo è che ho avuto un bug nel codice simile nel mio progetto, dove ho usato per scorrere una raccolta personalizzata da una libreria esterna, che è stata chiamata come SomeTypeCollection, ma in realtà ha fornito una raccolta di elementi di tipo base e avrebbe potuto contenere articoli di SomeOtherType. Colpa mia, ma ancora né la lingua, né il compilatore hanno fornito suggerimenti/avvertenze esplicite, cosa abbastanza inusuale per C# ...

+6

Heh, per prima cosa mi sono imbattuto in questo quando speravo (non mi aspettavo davvero) che foreach avrebbe implicitamente filtrato gli oggetti nella raccolta in base al loro tipo. Pensa a come legge "Per ogni stringa nella collezione". Per me, si legge: "Per ogni cosa nella collezione è una stringa, fai qualcosa". Quindi ignorerebbe tutto ciò che non era una stringa. –

risposta

22

ripenso a prima di farmaci generici ... foreach dovuto lanciare in modo che si possa fare cose sensibili come:

foreach (string x in names) 
{ 
    // ... 
} 

invece di:

foreach (object tmp in names) 
{ 
    string x = (string) tmp; 
    // ... 
} 

Quest'ultimo è appena icky, IMO. Fornire un cast implicito è diverso dal resto del linguaggio, ma lo rende molto più facile da usare nella stragrande maggioranza dei casi.

ho il sospetto che se C# avesse avuto generici e metodi di estensione per iniziare (e quindi abbiamo potuto usare OfType e Cast) che foreach non deve essere specificato in tutto allo stesso modo.

Nota che c'è ancora più stranezza in foreach: il tipo non deve implementare IEnumerable affatto. Finché ha un metodo GetEnumerator che restituisce qualcosa che a sua volta ha MoveNext() e Current, il compilatore C# è felice. Ciò significava che era possibile implementare un "iteratore fortemente tipizzato" (per evitare la boxe) prima dei generici.

+0

Giusto. Sospettavo questo è un patrimonio dal primo C# e rendendo fortemente tipizzato ora è un cambiamento di rottura ... [brontolare] ancora, ci avrebbe potuto essere un avvertimento o solo una nota in MSDN :-) –

+0

Va bene, di più aggiornato ora e in grado di capire ... beh ... parole ... non pensare che la mia risposta abbia aggiunto qualcosa che non è stato detto qui :) – jerryjvl

+1

Potrebbero costruire funzionalità rilevanti senza generici invece di implementare questo ridicolo cast implicito . – Trismegistos

9

Con C# 3 Sto usando var - così ricevo gli avvisi del compilatore.

+0

Buon punto. L'uso di var in foreach può salvarti da quel bug, poiché rileverà il tipo esatto della raccolta su cui stai iterando. –

4

foreach funziona su IEnumerable, che restituisce oggetti di tipo oggetto. Per ogni oggetto, l'oggetto sarà castato per il tipo che hai fornito.

A meno che non si usi var in C# 3.0. Quindi il tipo verrà preso da IEnumerable<T>, se implementato dalla raccolta.

+2

Ciò dà l'impressione che foreach (int x in List ) boxing e quindi unbox - che non è. Il compilatore C# utilizza IEnumerable in preferenza su IEnumerable dove è disponibile. –

+0

hai ragione sull'interfaccia digitata.A volte le specifiche C# sono un po 'incoerenti: sembra che il costrutto foreach sia l'unica cosa in C# che consente la covarianza e la controvarianza. –

Problemi correlati