c# - que - Devuelve un IEnumerator vacío
yield return c# stack overflow (9)
Tengo una interfaz que, entre otras cosas, implementa un método "IEnumerator GetEnumerator () público", por lo que puedo usar la interfaz en una instrucción foreach.
Implemento esta interfaz en varias clases y en una de ellas, quiero devolver un IEnumerator vacío. Ahora mismo hago esto de la siguiente manera:
public IEnumerator GetEnumerator()
{
ArrayList arr = new ArrayList();
return arr.GetEnumerator();
}
Sin embargo, considero que esto es un truco feo, y no puedo evitar pensar que hay una mejor manera de devolver un IEnumerator vacío. ¿Esta ahí?
Encontré esta pregunta buscando la forma más sencilla de obtener un enumerador vacío. Después de ver la respuesta comparando el rendimiento, decidí usar la solución de clase de enumerador vacío, pero la mía es más compacta que los otros ejemplos, es de tipo genérico y también proporciona una instancia predeterminada para que no tenga que crear nuevas instancias. El tiempo, 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() { }
}
Esto es simple en C # 2:
public IEnumerator GetEnumerator()
{
yield break;
}
Necesitará la declaración de yield break
del yield break
para forzar al compilador a que lo trate como un bloque iterador.
Esto será menos eficiente que un iterador vacío "personalizado", pero es un código más simple ...
Hay una función extra en el marco:
public static class Enumerable
{
public static IEnumerable<TResult> Empty<TResult>();
}
puedes escribir
var NullEnumerable = Enumerable.Empty<int>();
La forma en que uso es usar el enumerador de una matriz vacía:
public IEnumerator GetEnumerator() {
return new object[0].GetEnumerator();
}
También se puede usar para IEnumerator genérico o IEnumerable (use una matriz del tipo apropiado)
Lo escribí así:
public IEnumerator<T> GetEnumerator()
{
return this.source?.GetEnumerator() ??
Enumerable.Empty<T>().GetEnumerator();
}
Podría implementar una clase ficticia que implemente 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()
{
}
}
Puede hacer un NullEnumerator que implemente la interfaz IEnumerator. Solo puedes pasar una instancia del NullEnumerator.
here hay un ejemplo de un EmptyEnumerator
Puede implementar la interfaz IEnumerator y IEnumerable, y devolver falso desde 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();
}
}
Tenía curiosidad y fui un poco más allá. Hice una prueba que verifica qué tan eficientes son los métodos que comparan el yield break
, el Enumerable.Emtpy
y la clase personalizada.
Puede verlo en dotnetfiddle https://dotnetfiddle.net/vTkmcQ o usar el código a continuación.
El resultado de una de las muchas ejecuciones dotnetfiddle con 190 000 iteraciones fue:
Interrupción de rendimiento: 00: 00: 00.0210611
Enumerable.Empty (): 00: 00: 00.0192563
Instancia 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; } }
}
}