Pregunta

¿Hay alguna forma de usar bloques de rendimiento para implementar un IEnumerator<T> que pueda ir hacia atrás (MoveLast()) así como hacia adelante?

¿Fue útil?

Solución

No directamente desde el bloque iterador, no.

Sin embargo, la persona que llama siempre puede almacenar en búfer los resultados, por ejemplo, en un List<T>, o simplemente llamar a Reverse(), pero esto no siempre se aplica.

Otros consejos

No, la máquina de estado generada por el compilador de C # es estrictamente hacia adelante.

Ni siquiera tiene sentido retroceder en muchos casos. Imagine un iterador leyendo de una secuencia de red: para ir hacia atrás, tendría que recordar todo lo que había leído alguna vez, porque no podía rebobinar el tiempo y pedir nuevamente a la red los datos.

(Lo mismo ocurre con cualquier cosa que genere datos de alguna forma con pérdida. Imagine un iterador que devuelve una nueva placa para Conway's Life en cada iteración; hay varias placas que podrían haber sido anteriores , así que para retroceder, debe recordar lo que ya ha devuelto).

Sé que este hilo es muy antiguo, pero es importante tener en cuenta que

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

... se compila en:

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

Entonces, si no le importa el " MoveNext " sintaxis, puede implementar fácilmente IEnumerator y agregar " MovePrevious " ;. No podría invertir la dirección si usa & Quot; foreach & Quot; pero podría invertir la dirección si usa un ciclo while.

O ... si quieres " foreach " una lista en dirección inversa (no bidireccional) podría aprovechar la declaración de rendimiento.

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];
}

O ... si desea predecir a la inversa siguiendo la ruta larga, puede implementar su propio 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 de colecciones C5 ( http://www.itu.dk/research/c5/) implementa colecciones y listas vinculadas con enumeración hacia atrás. El proyecto es OpenSource, por lo que debería poder encontrar la respuesta allí.

No. Una de las limitaciones de IEnumerator es que mantiene su estado actual y no recuerda su estado anterior. Como resultado, IEnumerable es solo de avance.

Si necesita conservar los estados anteriores, lea IEnumerable en una Lista o LinkedList y enumere esos objetos en su lugar.

En realidad, parece haber un enfoque descrito en C # 2008 acelerado . Desafortunadamente, dos páginas no son visibles en la vista previa, y tiene que depender de la reflexión (cuyos resultados se pueden almacenar en caché, como de costumbre), pero puede obtener la esencia.

No. El uso de yield da como resultado un IEnumerable que es unidireccional.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top