Implementazione di un enumeratore bidirezionale in C #

Esiste un modo per utilizzare i blocchi di rendimento per implementare un IEnumerator che può andare indietro ( MoveLast() ) e inoltrare?

    Non direttamente dal blocco iteratore, no.

    Tuttavia, il chiamante può sempre bufferizzare i risultati, ad esempio in un List , o semplicemente chiamare Reverse() – ma questo non si applica sempre.

    No, la macchina a stati generata dal compilatore C # è strettamente in avanti.

    Non ha nemmeno senso tornare indietro in molti casi. Immagina un iteratore che legge da un stream di rete – per tornare indietro, dovrebbe ricordare tutto ciò che aveva letto, perché non poteva riavvolgere il tempo e chiedere di nuovo alla rete i dati.

    (Ditto tutto ciò che ha generato dati in qualche modo fatiscente. Immagina un iteratore che ha restituito una nuova scheda per Conway’s Life ad ogni iterazione: ci sono più tabs che potevano essere tutte precedenti , quindi per tornare indietro devi ricordare quello che sei già tornato.)

    So che questo thread è super vecchio, ma è importante notare che

     foreach(var item in someCollection) { // Do something } 

    … viene compilato in:

     var enumerator = someCollection.GetEnumerator() while (enumerator.MoveNext()) { var item = enumerator.Current; // Do something } 

    Quindi, se non ti dispiace la syntax “MoveNext”, potresti facilmente implementare IEnumerator e aggiungere un “MovePrevious”. Non si sarebbe in grado di invertire la direzione se si utilizza “foreach”, ma si sarebbe in grado di invertire la direzione se si utilizza un ciclo while.

    Oppure … se vuoi “foreach” una lista in direzione inversa (non bidirezionale) potresti sfruttare la dichiarazione di rendimento.

     public static IEnumerable Get(IList list) { if (list == null) yield break; for (int i = list.Count - 1; i > -1; i--) yield return list[i]; } 

    Oppure … se si vuole forzare al contrario andando lungo il percorso, è ansible implementare il proprio IEnumerable / IEnumerator

     public static class ReverseEnumerable { public static IEnumerable Get(IList list) { return new ReverseEnumerable(list); } } public struct ReverseEnumerable : IEnumerable { private readonly IList _list; public ReverseEnumerable(IList list) { this._list = list; } public IEnumerator GetEnumerator() { if (this._list == null) return Enumerable.Empty().GetEnumerator(); return new ReverseEnumator(this._list); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } public struct ReverseEnumator : IEnumerator { private readonly IList _list; private int _currentIndex; public ReverseEnumator(IList list) { this._currentIndex = list.Count; this._list = list; } public bool MoveNext() { if (--this._currentIndex > -1) return true; return false; } public void Reset() { this._currentIndex = -1; } public void Dispose() { } public TItem Current { get { if (this._currentIndex < 0) return default(TItem); if (this._currentIndex >= this._list.Count) return default(TItem); return this._list[this._currentIndex]; } } object IEnumerator.Current { get { return this.Current; } } } 

    La libreria C5 Collections ( http://www.itu.dk/research/c5/ ) implementa le raccolte e l’elenco collegato con l’enumerazione all’indietro. Il progetto è OpenSource quindi dovresti essere in grado di trovare una risposta.

    No. Uno dei limiti di IEnumerator è che mantiene il suo stato corrente e non ricorda il suo stato precedente. Di conseguenza, IEnumerable è di tipo forward-only.

    Se è necessario mantenere gli stati precedenti, leggere l’object IEnumerable in un elenco o LinkedList ed enumerare invece tramite tali oggetti.

    In realtà, sembra esserci un approccio descritto in Accelerated C # 2008 . Sfortunatamente, due pagine non sono visibili nell’anteprima e devono fare affidamento sulla riflessione (i cui risultati possono essere memorizzati nella cache, come al solito), ma è ansible ottenere l’essenza.

    No. Usando i risultati di yield in un object IEnumerable che è unidirezionale.