Question

Existe-t-il un moyen d'utiliser des blocs de rendement pour implémenter un IEnumerator<T> pouvant aller en arrière (MoveLast()) et en avant?

Était-ce utile?

La solution

Pas directement à partir du bloc d'itérateur, n °

Cependant, l'appelant peut toujours mettre les résultats en mémoire tampon, par exemple dans un List<T>, ou simplement appeler Reverse() - mais cela ne s'applique pas toujours.

Autres conseils

Non, la machine à états générée par le compilateur C # est strictement en avant.

Dans de nombreux cas, il n’a même pas de sens de revenir en arrière. Imaginez un itérateur lisant un flux réseau - pour revenir en arrière, il devrait se rappeler de tout ce qu’il avait lu auparavant, car il ne pouvait pas revenir en arrière et demander à nouveau les données au réseau.

(Idem pour tout ce qui génère des données avec une certaine perte. Imaginez un itérateur qui renvoie un nouveau tableau pour Life de Conway à chaque itération - il existe plusieurs tableaux qui auraient tous pu être les précédents , donc pour revenir en arrière, vous devez encore vous rappeler ce que vous avez déjà rendu.)

Je sais que ce fil est très ancien, mais il est important de noter que

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

... est compilé dans:

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

Donc si ça ne vous dérange pas, le " MoveNext " syntaxe, vous pouvez facilement implémenter IEnumerator et ajouter un & "; MovePrevious &"; Vous ne pourrez pas inverser la direction si vous utilisez & "; Foreach &"; mais vous seriez capable d'inverser la direction si vous utilisez une boucle while.

Ou ... si vous voulez & "foreach &"; une liste en sens inverse (non bidirectionnelle), vous pouvez tirer parti de la déclaration de rendement.

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 ... si vous souhaitez éviter les erreurs en sens inverse, vous pouvez implémenter votre propre 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; }
    }
}

Bibliothèque de collections C5 ( http://www.itu.dk/research/c5/) implémente des collections et des listes chaînées avec énumération en arrière. Le projet est OpenSource, vous devriez donc pouvoir y répondre.

Non. L'une des limites d'IEnumerator est qu'il conserve son état actuel et ne se souvient pas de son état antérieur. En conséquence, IEnumerable est en aval uniquement.

Si vous devez conserver des états antérieurs, lisez l'IEnumerable dans une liste ou une liste liée et énumérez plutôt ces objets.

En fait, il semble y avoir une approche décrite dans C # 2008 accéléré . Malheureusement, deux pages ne sont pas visibles dans l'aperçu et il faut compter sur la réflexion (les résultats peuvent être mis en cache, comme d'habitude), mais vous pouvez en obtenir l'essentiel.

Non. L'utilisation de yield donne lieu à un IEnumerable qui est unidirectionnel.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top