Domanda

Esiste un modo per utilizzare i blocchi di rendimento per implementare un IEnumerator<T> che può andare indietro (MoveLast()) oltre che in avanti?

È stato utile?

Soluzione

Non direttamente dal blocco iteratore, no.

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

Altri suggerimenti

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

In molti casi non ha nemmeno senso tornare indietro. Immagina un iteratore che legge da un flusso di rete: per tornare indietro, dovrebbe ricordare tutto ciò che aveva mai letto, perché non poteva riavvolgere il tempo e chiedere nuovamente i dati alla rete.

(Idem qualsiasi cosa abbia generato dati in qualche modo con perdita di dati. Immagina un iteratore che ha restituito una nuova scheda per Conway's Life su ogni iterazione - ci sono più schede che avrebbero potuto essere tutte precedenti , quindi per tornare indietro devi ancora ricordare cosa sei già tornato.)

So che questa discussione è super vecchia 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 il " MoveNext " sintassi, puoi facilmente implementare IEnumerator e aggiungere un " MovePrevious " ;. Non saresti in grado di invertire la direzione se usi & Quot; foreach & Quot; ma saresti in grado di invertire la direzione se usi un ciclo while.

Oppure ... se vuoi " foreach " un elenco in direzione inversa (non bidirezionale) che potresti trarre vantaggio dall'istruzione yield.

public static IEnumerable<TItem> Get<TItem>(IList<TItem> list)
{
    if (list == null)
        yield break;

    for (int i = list.Count - 1; i > -1; i--)
        yield return list[i];
}

Oppure ... se vuoi fare una retromarcia seguendo il percorso lungo puoi implementare il tuo IEnumerable / IEnumerator

public static class ReverseEnumerable
{
    public static IEnumerable<TItem> Get<TItem>(IList<TItem> list)
    {
        return new ReverseEnumerable<TItem>(list);
    }
}

public struct ReverseEnumerable<TItem> : IEnumerable<TItem>
{
    private readonly IList<TItem> _list;

    public ReverseEnumerable(IList<TItem> list)
    {
        this._list = list;
    }

    public IEnumerator<TItem> GetEnumerator()
    {
        if (this._list == null)
            return Enumerable.Empty<TItem>().GetEnumerator();

        return new ReverseEnumator<TItem>(this._list);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

public struct ReverseEnumator<TItem> : IEnumerator<TItem>
{
    private readonly IList<TItem> _list;
    private int _currentIndex;

    public ReverseEnumator(IList<TItem> 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; }
    }
}

Biblioteca collezioni C5 ( http://www.itu.dk/research/c5/) implementa le raccolte e l'elenco collegato con enumerazione all'indietro. Il progetto è OpenSource, quindi dovresti essere in grado di trovare la risposta lì.

No. Una delle limitazioni di IEnumerator è che mantiene il suo stato corrente e non ricorda il suo stato precedente. Di conseguenza, IEnumerable è solo forward.

Se è necessario mantenere gli stati precedenti, leggere IEnumerable in un Elenco o LinkedList ed enumerare invece tali oggetti.

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

No. Utilizzando yield si ottiene un IEnumerable che è unidirezionale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top