Pregunta

Tengo una interfaz que, entre otras cosas, implementa un método "IEnumerator GetEnumerator pública ()", por lo que puede utilizar la interfaz en una instrucción foreach.

Me implementar esta interfaz en varias clases y en una de ellas, quiero devolver un IEnumerator vacía. En este momento hago esto de la siguiente manera:

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

Sin embargo, considero esto un truco feo, y no puedo evitar pensar que hay una mejor manera de devolver un IEnumerator vacía. ¿Hay?

¿Fue útil?

Solución

Esto es simple en C # 2:

public IEnumerator GetEnumerator()
{
    yield break;
}

Es necesario la declaración yield break para forzar al compilador a tratarlo como un bloque de iterador.

Esto será menos eficiente que una "costumbre" iterador vacío, pero es más simple código ...

Otros consejos

Existe una función adicional en el marco:

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

El uso de este puede escribir:

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

Se podría implementar una clase que implementa IEnumerator ficticia, y devolver una instancia de ella:

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

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}

Me dio curiosidad y fui un poco más lejos. Hice una prueba que comprueba el grado de eficiencia de los métodos están comparando yield break, Enumerable.Emtpy y clase personalizada.

Puede encontrar esta información en https://dotnetfiddle.net/vTkmcQ o usar el código de abajo.

El resultado de uno de los muchos dotnetfiddle ejecuta utilizando 190 000 iteraciones fue:

  

ruptura Rendimiento: 00: 00: 00,0210611

     

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

     

EmptyEnumerator ejemplo: 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 forma en que uso es utilizar el empadronador de una matriz vacía:

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

También se puede utilizar para IEnumerator genérico o IEnumerable (utilizar una matriz del tipo apropiado)

Puede implementar la interfaz IEnumerator y IEnumerable, y volver falsa de la función 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();
    }
}

Lo escribí como esto:

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

Se puede hacer una NullEnumerator que implementa la interfaz IEnumerator. Que sólo puede pasar una instancia de la NullEnumerator.

aquí es un ejemplo de un EmptyEnumerator

Se han encontrado esta pregunta en busca de la forma más sencilla de conseguir un enumerador vacía. Después de ver la respuesta comparando el rendimiento decidí usar la solución de clase empadronador vacía, pero la mía es más compacto que los otros ejemplos, y es un tipo genérico, y también proporciona una instancia predeterminada por lo que no tiene que crear nuevas instancias todas las tiempo, lo que debería mejorar aún más el rendimiento.

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() { }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top