C'è molta confusione nelle altre risposte finora. (Anche se la risposta di Preston Guillot è piuttosto buona, in realtà non mette un dito su quello che sta succedendo qui.) Lasciami provare a chiarire.
Primo spento, siete semplicemente sfortunati. C# richiede che la raccolta utilizzata in un'istruzione foreach sia:
- Implementare un numero pubblico
GetEnumerator
corrispondente al modello richiesto.
- Implementare
IEnumerable
(e, naturalmente, richiede IEnumerable<T>
IEnumerable
)
- essere dinamico, in questo caso abbiamo semplicemente calci il barattolo lungo la strada e fare l'analisi in fase di esecuzione.
Il risultato è che il tipo di raccolta deve effettivamente implementare modo GetEnumerator
uno o l'altro. Fornire un metodo di estensione non lo taglia.
Questo è un peccato. A mio parere, quando il team C# ha aggiunto i metodi di estensione al C# 3, dovrebbero aver modificato le funzionalità esistenti come foreach
(e forse anche using
!) Per prendere in considerazione i metodi di estensione. Tuttavia, la pianificazione era estremamente serrata durante il ciclo di rilascio del C# 3 e gli eventuali elementi di lavoro extra che non avevano implementato LINQ in tempo potrebbero essere tagliati. Non ricordo esattamente cosa ha detto il team di design su questo punto e non ho più i miei appunti.
Questa sfortunata situazione è il risultato del fatto che le lingue crescono e si evolvono; le vecchie versioni sono progettate per le esigenze del loro tempo e le nuove versioni devono basarsi su queste fondamenta. Se, controfattualmente, C# 1.0 aveva avuto metodi di estensione e generici, allora il ciclo foreach
avrebbe potuto essere progettato come LINQ: come una semplice trasformazione sintattica. Ma non lo era, e ora siamo bloccati con il retaggio di pre-generico, design del metodo di pre-estensione.
Secondo, sembra esserci qualche disinformazione in altre risposte e commenti su ciò che è esattamente necessario per rendere operativo foreach
. Non è necessario implementare IEnumerable
. Per ulteriori dettagli su questa caratteristica comunemente fraintesa, vedere my article on the subject.
Terzo, sembra esserci qualche domanda sul fatto che questo comportamento sia effettivamente giustificato dalla specifica. È. Le specifiche non indicano esplicitamente che i metodi di estensione non sono considerati in questo caso, il che è sfortunato. Tuttavia, la specifica è estremamente chiara su cosa succede:
Il compilatore inizia eseguendo una ricerca membro per GetEnumerator
. L'algoritmo di ricerca dei membri è documentato in dettaglio nella sezione 7.3 e la ricerca dei membri non considera i metodi di estensione, solo i membri effettivi . I metodi di estensione sono considerati solo dopo che la normale risoluzione di sovraccarico ha avuto esito negativo e non abbiamo ancora ottenuto la risoluzione di sovraccarico. (E sì, i metodi di estensione sono considerati da accesso membri, ma membri accesso e membro ricerca sono diverse operazioni.)
Se lookup membro non trovare un gruppo metodo poi il tentativo di abbinare il il modello fallisce.Il compilatore pertanto non passa mai alla parte della risoluzione di sovraccarico dell'algoritmo e pertanto non ha mai la possibilità di prendere in considerazione i metodi di estensione.
Pertanto, il comportamento che descrivi è coerente con il comportamento specificato.
io consiglio di leggere la sezione 8.8.4 della specifica molta attenzione se si vuole capire con precisione come un compilatore analizza una dichiarazione foreach
.
Quarto, vi incoraggio a passare il vostro tempo aggiungendo valore al vostro programma in qualche altro modo. Il vantaggio convincente di
foreach (var row in table)
oltre
foreach(var row in table.Rows)
è piccolo per lo sviluppatore e invisibile al cliente. Trascorri il tuo tempo aggiungendo nuove funzionalità o correggendo bug o analizzando le prestazioni, piuttosto che abbreviare di cinque caratteri il codice già perfettamente chiaro.
Mi sembra che il messaggio di errore dice tutto. È necessario implementarlo sulla classe, non come estensione. – spender
Sono confuso .. che non può essere il codice reale. 'for'non funziona sull'enumeratore. E 'test' non è un enumerabile. Incolla il codice attuale –
Nota molto soggettiva: creare codice che sembri comune (come 'var row in table.Rows') su un gran numero di file sorgente e di campioni in codice che * senti ora per essere più bello * non è necessario il modo migliore di creare codice più leggibile. 'Foreach' spesso può essere trasformato in istruzione LINQ che è più compatta e abbastanza comune da non mettere in discussione ciò che sta accadendo. Immagina di postare il tuo 'foreach (var row in table)' nella prossima domanda su SO - nessuno potrà ragionare su quel codice senza un campione più grande che includa le tue estensioni magiche. –