Question

J'ai une interface qui, entre autres, met en œuvre une méthode « IEnumerator publique GetEnumerator () », donc je peux utiliser l'interface dans une instruction foreach.

J'implémentent cette interface dans plusieurs classes et dans l'un d'eux, je veux retourner un IEnumerator vide. En ce moment je fais de la manière suivante:

public IEnumerator GetEnumerator()
{
    ArrayList arr = new ArrayList();
    return arr.GetEnumerator();
}

Cependant, je trouve ce bidouille horrible, et je ne peux pas empêcher de penser qu'il ya une meilleure façon de retourner un IEnumerator vide. Est-il?

Était-ce utile?

La solution

est simple en C # 2:

public IEnumerator GetEnumerator()
{
    yield break;
}

Vous avez besoin de la déclaration de yield break pour forcer le compilateur à le traiter comme un bloc itérateur.

Ce sera moins efficace qu'une « mesure » iterator vide, mais il est plus simple ... le code

Autres conseils

Il y a une fonction supplémentaire dans le cadre:

public static class Enumerable
{
    public static IEnumerable<TResult> Empty<TResult>();
}

En utilisant cela, vous pouvez écrire:

var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();

Vous pouvez implémenter une classe fictive qui implémente IEnumerator et retourner une instance de celui-ci:

class DummyEnumerator : IEnumerator
{
    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}

J'étais curieux et je suis allé un peu plus loin. J'ai fait un test qui vérifie la façon efficace les méthodes comparent yield break, Enumerable.Emtpy et classe personnalisée.

Vous pouvez le vérifier sur https://dotnetfiddle.net/vTkmcQ ou utiliser le code ci-dessous.

Le résultat de l'un des nombreux dotnetfiddle essais utilisant 190 000 itérations est:

  

Pause Rendement: 00: 00: 00,0210611

     

Enumerable.Empty (): 00: 00: 00,0192563

     

EmptyEnumerator par exemple: 00: 00: 00,0012966

using System;
using System.Diagnostics;
using System.Collections;
using System.Linq;

public class Program
{
    private const int Iterations = 190000;
    public static void Main()
    {
        var sw = new Stopwatch();

        sw.Start();
        for (int i = 0; i < Iterations; i++)
        {
            IEnumerator enumerator = YieldBreak();
            while(enumerator.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();

        Console.WriteLine("Yield break: {0}", sw.Elapsed);

        GC.Collect();

        sw.Restart();
        for (int i = 0; i < Iterations; i++)
        {
            IEnumerator enumerator = Enumerable.Empty<object>().GetEnumerator();
            while(enumerator.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();

        Console.WriteLine("Enumerable.Empty<T>(): {0}", sw.Elapsed);

        GC.Collect();

        sw.Restart();
        var instance = new EmptyEnumerator();
        for (int i = 0; i < Iterations; i++)
        {
            while(instance.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();

        Console.WriteLine("EmptyEnumerator instance: {0}", sw.Elapsed);
    }

    public static IEnumerator YieldBreak()
    {
        yield break;
    }

    private class EmptyEnumerator : IEnumerator
    {
        //public static readonly EmptyEnumerator Instance = new EmptyEnumerator();

        public bool MoveNext()
        {
            return false;
        }

        public void Reset()
        {
        }

        public object Current { get { return null; } }
    }
}

La façon dont j'utilise est d'utiliser l'énumérateur d'un tableau vide:

public IEnumerator GetEnumerator() {
    return new object[0].GetEnumerator();
}

Il peut également être utilisé pour IEnumerator générique ou IEnumerable (utiliser un tableau de type approprié)

Vous pouvez implémenter l'interface IEnumerator et IEnumerable et return false de la fonction MoveNext de interfase IEnumerable

private class EmptyEnumerator : IEnumerator
{


    public EmptyEnumerator()
    {
    }

    #region IEnumerator Members

    public void Reset() { }

    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }
    public bool MoveNext()
    { return false; }
}


public class EmptyEnumerable : IEnumerable
{

    public IEnumerator GetEnumerator()
    {
        return new EmptyEnumerator();
    }
}

Je l'ai écrit comme ceci:

public IEnumerator<T> GetEnumerator()
{
    return this.source?.GetEnumerator() ??
            Enumerable.Empty<T>().GetEnumerator();
}

Vous pouvez faire une NullEnumerator qui implémente l'interface IEnumerator. Vous pouvez juste passer une instance de l'NullEnumerator.

est un exemple d'un EmptyEnumerator

trouvé cette question à la recherche de la façon la plus simple d'obtenir un recenseur vide. Après avoir vu la réponse comparer les performances que j'ai décidé d'utiliser la solution de classe recenseur vide, mais le mien est plus compact que les autres exemples, et est un type générique, et fournit également une instance par défaut de sorte que vous ne devez pas créer de nouvelles instances tous les temps, ce qui devrait améliorer encore la performance.

class EmptyEnumerator<T> : IEnumerator<T>
{
   public readonly static EmptyEnumerator<T> value = new EmptyEnumerator<T>();
   public T Current => throw new InvalidOperationException();
   object IEnumerator.Current => throw new InvalidOperationException();
   public void Dispose() { }
   public bool MoveNext() => false;
   public void Reset() { }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top