Frage

Ich habe eine Schnittstelle, die unter anderem, implementiert eine "public IEnumerator GetEnumerator ()" Methode, so dass ich die Schnittstelle in einer foreach-Anweisung verwenden kann.

ich diese Schnittstelle in mehreren Klassen implementieren und in einem von ihnen, ich will eine leere IEnumerator zurückzukehren. Im Moment tue ich dies auf folgende Weise:

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

Allerdings halte ich diese eine hässliche Hack, und ich kann nicht umhin denken, dass es einen besseren Weg, eine leere IEnumerator zurückzukehren. Gibt es?

War es hilfreich?

Lösung

Das ist einfach in C # 2:

public IEnumerator GetEnumerator()
{
    yield break;
}

Sie müssen die yield break Anweisung, um die Compiler zu zwingen, sie als Iteratorblock zu behandeln.

Dies wird weniger effizient als ein „custom“ leer Iterator, aber es ist einfacher Code ...

Andere Tipps

Es wird eine zusätzliche Funktion im Rahmen:

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

Mit diesem können Sie schreiben:

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

Sie können eine Dummy-Klasse implementieren, dass implementiert IEnumerator und geben eine Instanz davon:

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

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}

Ich war neugierig und ging ein Stück weiter. Ich habe einen Test, dass die Kontrollen, wie effizient die Methoden vergleichen yield break, Enumerable.Emtpy und benutzerdefinierte Klasse.

Sie können es heraus dotnetfiddle https://dotnetfiddle.net/vTkmcQ oder den Code unten verwenden.

Das Ergebnis eines der vielen dotnetfiddle Läufen mit 190 000 Iterationen war:

  

Yield Pause: 00: 00: 00,0210611

     

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

     

EmptyEnumerator Beispiel: 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; } }
    }
}

So wie ich Gebrauch ist den enumerator eines leeren Array zu verwenden:

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

Es kann auch für generische IEnumerator oder IEnumerable verwendet werden (ein Array des entsprechenden Typs verwenden)

Sie können IEnumerator-Schnittstelle und IEnumerable implementieren und return false von Movenext-Funktion von IEnumerable Interfase

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();
    }
}

Ich schrieb es wie folgt aus:

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

Sie können einen NullEnumerator machen, die die IEnumerator-Schnittstelle implementiert. Sie können nur eine Instanz von dem NullEnumerator passieren.

hier ist ein Beispiel für eine EmptyEnumerator

Gefunden diese Frage für den einfachsten Weg, um eine leere enumerator zu bekommen. Nachdem die Antwort zu sehen, zum Vergleich der Leistung habe ich beschlossen, die leere enumerator Class-Lösung zu verwenden, aber ich ist kompakter als die anderen Beispiele, und ist ein generischer Typ, und stellt auch eine Standardinstanz, so dass Sie neue Instanzen nicht alle schaffen müssen, um die Zeit, die noch weiter Leistung verbessern sollte.

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() { }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top