2014-04-18 14 views
10

Seguendo due metodi (uno usa IEnumerator<int> e altri usi List<int>.Enumerator) anche se sembra identico produce risultati diversi.Comportamento del metodo di ripristino dell'elenco <T> .Enumeratore

static void M1() 
{ 
    var list = new List<int>() { 1, 2, 3, 4 }; 
    IEnumerator<int> iterator = list.GetEnumerator(); 
    while (iterator.MoveNext()) 
    { 
    Console.Write(iterator.Current); 
    } 
    iterator.Reset(); 
    while (iterator.MoveNext()) 
    { 
     Console.Write(iterator.Current); 
    } 
} 
static void M2() 
{ 
    var list = new List<int>() { 1, 2, 3, 4 }; 
    //Here the iterator will be List<int>.Enumerator (which is a struct) 
    var iterator = list.GetEnumerator(); 
    while (iterator.MoveNext()) 
    { 
    Console.Write(iterator.Current); 
    } 
    //This will not work, as Reset method was implemented explicitly 
    //iterator.Reset(); 

    //So casting it to IEnumerator is required 
    //which will lead to boxing and other issues of struct and interface 
    ((IEnumerator<int>)iterator).Reset(); 

    //Following loop will NOT work 
    while (iterator.MoveNext()) 
    { 
    Console.Write(iterator.Current); 
    } 
} 

Ci sono un paio di domande che spiega chiaramente questo comportamento, li here, here, e here si può controllare.

ho ancora seguendo due dubbi

  1. Perché List.Enumerator non getta "NotSupportedException" per Reset?
  2. Perché Reset è stato implementato in modo esplicito e non implicitamente come MoveNext e Current?
+1

Non dovrebbe questo ultimo commento dire "Il ciclo seguente non funzionerà"? Oppure potrebbe essere più illustrativo se dicesse "Il ciclo seguente viene saltato senza eseguire". – JLRishe

+0

@JLRishe - Sì, l'ho perso, corretto ora. Grazie. –

risposta

7

Perché List.Enumerator non lancia "NotSupportedException" per Reset?

Perché Microsoft non aveva una macchina del tempo per prevedere cosa sarebbe successo 5 anni dopo. Il forte impulso alla base dell'inferenza di tipo era Linq, ma non era sulla mappa stradale verso la fine degli anni '90, quando i generici venivano inizialmente lavorati. Il problema di boxe non è un problema senza di esso.

Perché Reset è stato implementato in modo esplicito e non implicitamente come MoveNext e Current?

Poiché non è possibile annullare l'ereditarietà di un metodo di interfaccia, è possibile solo nasconderlo. Che IEnumerator abbia un metodo Reset() è un altro problema di macchina del tempo, questo è stato deciso nel 1995, quando è stata progettata l'automazione COM. Circa un altro divario di 5 anni tra scelta e conseguenze :) .NET aveva per fornire una mappatura decente tra COM iterators e iteratori .NET per avere una possibilità di combattere per essere adottata.

Come si può vedere dal collegamento, un'altra funzionalità negli iteratori COM è la clonazione. Quale fu l'impulso dietro l'interfaccia ICloneable, un'altra interfaccia molto fastidiosa in .NET. Quello era troppo difficile da implementare nei loro fratelli generici, solo gli enumeratori di raccolta non generici lo implementano.

Microsoft ha un lavoro difficile, ogni decisione di progettazione è una con cui devono convivere per sempre.La nostra è molto più semplice, semplicemente non possiamo usare Reset :)

+0

Grazie per aver fornito informazioni storiche. Per i generici intendi i primi anni 2000, da quello che ricordo. Net si stava sviluppando intorno al 2000-01. –

+1

I generici .NET sono stati [avviati nel 1999] (http://blogs.msdn.com/b/dsyme/archive/2011/03/15/net-c-generics-history-some-photos-from-feb-1999 aspx). Ci sono voluti fino al 2004 prima che diventasse abbastanza stabile. Altri 5 anni ... –

+0

Bella risposta! Ho anche una prima edizione di MS Press C#, che spiega già i generici, anche se non era ancora presente in .NET 1.1. :-) Comunque, mi sono chiesto se 'yield' avesse qualcosa a che fare con esso (più di Linq). – atlaste

2

Perché List.Enumerator non lancia "NotSupportedException" per Reset?

Perché dovrebbe? List<T> è un tipo per il quale è semplice implementare Reset, quindi perché non implementarlo?

Perché Reset è stato implementato in modo esplicito e non implicitamente come MoveNext e Current?

Penso che sia perché Reset è generalmente considerato un errore ora. Ma esiste, quindi deve essere implementato in qualche modo. E quindi nasconderlo usando un'implementazione esplicita dell'interfaccia, dice "probabilmente non dovresti usarlo".