Pergunta

Existe uma maneira de blocos de rendimento usar para implementar um IEnumerator<T> que pode ir para trás (MoveLast()), bem como para a frente?

Foi útil?

Solução

Não diretamente a partir do bloco iterador, não.

No entanto, o chamador sempre pode amortecer os resultados, por exemplo em um List<T>, ou apenas chamar Reverse() -. Mas isso nem sempre se aplica

Outras dicas

Não, a máquina de estado gerado pelo compilador C # é estritamente para a frente.

Ele não faz muito sentido ir para trás em muitos casos. Imagine um iterador leitura de um fluxo de rede - para ir para trás, ele teria que se lembrar de tudo que ele já tinha lido, porque ele não podia tempo retroceder e pedir a rede para os dados novamente

.

(Ditto qualquer coisa que os dados gerados de alguma forma com perdas Imagine um iterador que retornou um novo conselho para a vida de Conway em cada iteração -. Existem várias placas que poderiam ter sido o anterior um, então para ir para trás mais uma vez tem que lembrar o que você já retornou.)

Eu sei que esta discussão é super antiga, mas é relevante notar que

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

... é se compilados em:

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

Então, se você não se importa a sintaxe "MoveNext", você poderia facilmente implementar IEnumerator e adicionar um "MovePrevious". Você não seria capaz de inverter o sentido se você usar "foreach" mas você seria capaz de inverter a direção se estiver usando um loop while.

Ou ... se você quiser "foreach" uma lista em sentido inverso (não bidirecional) você poderia tirar proveito da declaração de rendimento.

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

Ou ... se você quiser foreach no sentido inverso, indo o longo caminho que você pode implementar seu próprio 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 C5 Collections ( http://www.itu.dk/research/c5/) implementa coleções e lista ligada com para trás enumeração. O projeto é OpenSource por isso você deve ser capaz de encontrar resposta lá.

No. Uma das limitações de IEnumerator é que ele mantém o seu estado atual, e ele não se lembra de seu estado anterior. Como resultado, IEnumerable é forward-only.

Se você precisar se agarrar estados anteriores, leia o IEnumerable em uma lista ou LinkedList e enumerar esses objetos em seu lugar.

Na verdade, parece haver uma abordagem descrita em Accelerated C # 2008 . Infelizmente, duas páginas não são visíveis na pré-visualização, e tem que confiar em reflexão (cujos resultados podem ser armazenadas em cache, como de costume), mas você pode obter a essência.

No. Usando yield resultados em uma IEnumerable que é unidirecional.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top