2009-11-11 11 views
43

Tengo una interfaz que, entre otras cosas, implementa un método "público IEnumerator GetEnumerator()", por lo que puedo usar la interfaz en una instrucción foreach.Devolver un IEnumerator vacío

Implemento esta interfaz en varias clases y en una de ellas, deseo devolver un IEnumerator vacío. 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. ¿Esta ahí?

Respuesta

64

Esto es simple en C# 2:

public IEnumerator GetEnumerator() 
{ 
    yield break; 
} 

Necesita la instrucción yield break para forzar al compilador a tratarlo como un bloque de iterador.

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

+2

No tiene sentido tratar de competir contigo en una pregunta sobre C#. :-) – Konamiman

+8

@Konamiman: No, en absoluto. ¿Recuerdas la pregunta de LINQ que respondiste y luego borraste, al detectar que se requería el valor del atributo? ¡Me lo perdí! –

0

Puede crear un NullEnumerator que implemente la interfaz IEnumerator. Puede pasar una instancia de NullEnumerator.

here es un ejemplo de un EmptyEnumerator

1

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

De acuerdo con la documentación de MSDN, actual debería lanzar una excepción en este caso (http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.current.aspx) – Konamiman

+0

Ok gracias @ Konamiman, corregido;) –

8

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

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

    public bool MoveNext() 
    { 
     return false; 
    } 

    public void Reset() 
    { 
    } 
} 
+0

Y el voto abajo es porque ...? – Konamiman

4

La forma en que uso es utilizar el enumerador de una matriz vacía:

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

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

+0

Me gusta un poco esta solución. Es sencillo. Es directo. Es económico. Es elegante (al menos por lo que parece). Me pregunto si hay algún inconveniente para usar algo como esto. Aún así, compraría de nuevo. – xDisruptor

65

Existe una función adicional en el marco:

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

puede escribir

var NullEnumerable = Enumerable.Empty<int>(); 
+11

Para obtener un IEnumerator en lugar de un IEnumerable : Enumerable.Empty () .GetEnumerator(); –

+0

Esto es asombroso. Gracias – Skadoosh

0

lo escribí así:

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

tenía curiosidad y fui un poco más lejos. Hice una prueba que comprueba qué tan eficientes son los métodos que comparan yield break, Enumerable.Emtpy y la clase personalizada.

Puede consultarlo en dotnetfiddle https://dotnetfiddle.net/vTkmcQ o utilice el siguiente código.

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

Rendimiento: 00: 00: 00,0210611

Enumerable.Empty(): 00:00:00.0192563

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

encontrado esta pregunta en busca de la forma más sencilla de conseguir un enumerador vacía. Después de ver la respuesta que compara el rendimiento, decidí usar la solución de la clase enumerador vacía, pero la mía es más compacta que los otros ejemplos, es un tipo genérico y también proporciona una instancia predeterminada para que no tenga que crear nuevas instancias. tiempo, lo que debería incluso 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() { } 
} 
Cuestiones relacionadas