Domanda

Ho un'interfaccia che, tra le altre cose, implementa un "pubblico IEnumerator GetEnumerator ()" il metodo, in modo da poter utilizzare l'interfaccia in una dichiarazione foreach.

a implementare questa interfaccia in diverse classi e in uno di essi, voglio restituire un IEnumerator vuota. In questo momento faccio questo modo seguente:

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

Comunque considero questo un brutto hack, e non posso fare a meno di pensare che ci sia un modo migliore di restituzione di un IEnumerator vuota. C'è?

È stato utile?

Soluzione

Questo è semplice in C # 2:

public IEnumerator GetEnumerator()
{
    yield break;
}

È necessario l'affermazione yield break per forzare il compilatore di trattarlo come un blocco iteratore.

Questa sarà meno efficiente di un "su misura" iteratore vuota, ma è il codice più semplice ...

Altri suggerimenti

C'è una funzione in più nel quadro:

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

Con questo è possibile scrivere:

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

Si potrebbe implementare una classe fittizio che implementa IEnumerator, e restituire un'istanza di esso:

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

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}

ero curioso e sono andato un po 'oltre. Ho fatto un test che verifica come efficiente i metodi stanno confrontando yield break, Enumerable.Emtpy e classe personalizzata.

È possibile controllare sul https://dotnetfiddle.net/vTkmcQ o utilizzare il codice qui sotto.

Il risultato di uno dei tanti dotnetfiddle viene eseguito utilizzando 190 000 iterazioni è stato:

  

pausa Resa: 00: 00: 00,0210611

     

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

     

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

Il modo in cui uso è quello di utilizzare l'enumeratore di un array vuoto:

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

Può essere utilizzato anche per IEnumerator generici o IEnumerable (utilizzare un array del tipo appropriato)

È possibile implementare l'interfaccia IEnumerator e IEnumerable, e restituire false dalla funzione MoveNext di 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();
    }
}

ho scritto in questo modo:

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

È possibile effettuare una NullEnumerator che implementa l'interfaccia IEnumerator. Si può solo passare un'istanza al largo della NullEnumerator.

qui è un esempio di un EmptyEnumerator

Trovato questa domanda cercando il modo più semplice per ottenere un enumeratore vuoto. Dopo aver visto la risposta confrontando le prestazioni ho deciso di utilizzare la soluzione di classe enumeratore vuoto, ma la mia è più compatto rispetto agli altri esempi, ed è un tipo generico, e fornisce anche un'istanza predefinita in modo da non dover creare nuove istanze tutte le tempo, che dovrebbe migliorare ulteriormente le prestazioni.

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() { }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top