2009-11-26 8 views
7

Fondamentalmente mi chiedo perché MS abbia deciso di implementare un enumeratore che supporta solo l'avanzamento: MoveNext().Qual è il motivo per cui le interfacce IEnumerable/IEnumerable <T> hanno solo MoveNext?

Non è più flessibile applicare anche MovePrevious per questa interfaccia ampiamente utilizzata in tutto il framework .NET?

Posso immaginare che il Linq.Reverse sia molto più semplice da implementare per MS e più efficiente in termini di prestazioni, ma non sono sicuro che ciò renda le altre cose più lente o sovraccarichi enormemente tutto il resto.

Chiunque abbia più conoscenze in materia può fornire maggiori informazioni al riguardo? vale a dire i pro e contro di avere o non avere MovePrevious in IEnumerable/IEnumerable<T>.

risposta

6

Esistono molti tipi di dati in cui non ha senso avere un MovePrevious. Ad esempio, se si ricevono dati da una connessione di rete, fornire un MovePrevious richiederebbe il buffering dell'intero stream nel caso in cui si chiamasse quel metodo. Ciò sprecherebbe enormi quantità di memoria.

Detto questo, potrebbe essere utile avere un tipo diverso che sia supportato MoveNext e MovePrevious (una doppia lista collegata potrebbe sostenere questo, per esempio).

+5

ITraversable ? :) –

+0

@MarkSimpson: mi sarebbe piaciuto avere visto un metodo 'Skip', insieme a una proprietà che avrebbe indicato se poteva accettare valori negativi. Nessun ragionevole 'IEnumerator' dovrebbe avere qualche difficoltà nell'implementazione di un metodo skip-forward-N-items, e per le collezioni basate su tree potrebbe consentire il recupero di elementi M da una collezione di N per prendere O (M + logN) piuttosto che O (MlogN) o O (N), che sono altrimenti il ​​meglio che si possa ottenere tramite 'IList '. – supercat

1

L'implementazione di un MovePrevious lo renderebbe molto più interfaccia. Mentre per alcune fonti (array, classi Container) un MovePrevious sarebbe banale, per molte altre fonti richiederebbe un buffering costoso, o escludere quelle fonti. I flussi di rete e le connessioni Database non supportano le operazioni di ricerca.

21

IEnumerable[<T>] rappresenta una sequenza di dati, non un elenco di accesso casuale. Non tutte le sequenze possono essere invertite o addirittura ripetute. Le sequenze basate su flussi di rete, l'accesso al database, ecc - o questa bellezza:

IEnumerable<int> GetData() { 
    Random rand = new Random(); 
    while(true) { yield return rand.Next(); } 
} 

Il meglio che puoi fare è ricominciare da capo - non chiamando Reset() (che è deprecato), ma ottenendo un enumeratore fresca, invece .

Anche senza Random è semplice creare sequenze semplici che non possono essere invertite (senza buffering e invertire il buffer). Per quello che vuoi, considera invece l'aspetto IList[<T>] - puoi accedere ai dati tramite l'indicizzatore in nell'ordine.

+0

Grazie Marc, se stai facendo il buffering che potrebbe anche essere un altro IEnumerable , potrebbe essere un overhead che non tutti vorrebbero, giusto? –

+1

Infatti, non chiamare le operazioni di buffering ('Reverse',' OrderBy', 'GroupBy') se si dispone di una sequenza grande (o infinita) ;-p –

+0

Per alcuni tipi di raccolte (ad esempio un albero o un elenco di salti) , una ricerca enumerabile sarebbe più efficiente di un 'IList '. Dato che qualsiasi implementazione ragionevole di 'IEnumerator' potrebbe implementare un' SkipAhead (int N) 'come' N' chiama a 'MoveNext', ma molti potrebbero implementarlo tramite mezzi più veloci, avendolo come parte di' IEnumerator 'sembrerebbe sensibile. – supercat

0

Ha a che fare con lo schema relativo alla "resa". Gli effettivi enumeratori sono la raccolta più semplice possibile, si inizia semplicemente in prima fila e si prende fino alla fine. Ciò significa che il codice per implementare l'enumerazione è estremamente semplice.

Inoltre è possibile avere il codice funzionale utilizzando iteratori che mai "fine" in quanto tale, per esempio

yield value++; 

Sarà semplicemente ricevere incrementare i numeri finché non si interrompe.

+1

In effetti non c'è bisogno di una "fine". Si consideri 'while (true) {yield return Random.Next(); } '. –

+3

Il design di IEnumerator è precedente alla resa di diversi anni. –

+0

'yield' semplifica l'implementazione, ma il concetto (di" raccolta più semplice possibile ") è altrettanto antico. Potrebbe essere formulato meglio, però. –

1

La convenienza nell'utilizzo non è l'unico fattore coinvolto nella progettazione di un'interfaccia ... è necessario tenere conto di quanto sarà versatile e di quali vincoli si aggiungono ad esso.

Ci sono sequenze che non possono essere riprodotte, e si dovrebbero aggiungere molti requisiti non necessari per l'implementazione.

Ci sono altre interfacce oltre, come IList, IQueryable. Utilizzando il più appropriato per lo scenario, comunica anche il tipo di utilizzo che dovrebbe avere.

Problemi correlati