Retornar um IEnumerator vazia
-
19-09-2019 - |
Pergunta
Eu tenho uma interface que, entre outras coisas, implementa um "público IEnumerator GetEnumerator ()" método, para que eu possa usar a interface em uma declaração foreach.
Eu implementar essa interface em várias classes e em um deles, eu quero voltar um IEnumerator vazio. Agora eu fazer isso da seguinte forma:
public IEnumerator GetEnumerator()
{
ArrayList arr = new ArrayList();
return arr.GetEnumerator();
}
No entanto, eu considero este um corte feio, e eu não posso ajudar, mas acho que há uma maneira melhor de retornar um IEnumerator vazio. Existe?
Solução
Esta é simples em C # 2:
public IEnumerator GetEnumerator()
{
yield break;
}
Você precisa o comunicado yield break
para forçar o compilador para tratá-la como um bloco iterador.
Este será menos eficiente do que uma "custom" iterador vazio, mas é código simples ...
Outras dicas
Há uma função extra no quadro:
public static class Enumerable
{
public static IEnumerable<TResult> Empty<TResult>();
}
Usando isso, você pode escrever:
var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();
Você poderia implementar uma classe fictícia que implementa IEnumerator, e retornar uma instância dele:
class DummyEnumerator : IEnumerator
{
public object Current
{
get
{
throw new InvalidOperationException();
}
}
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
}
Eu estava curioso e foi um pouco mais longe. Eu fiz um teste que verifica a eficiência dos métodos está comparando yield break
, Enumerable.Emtpy
e personalizado classe.
Você pode verificá-la no dotnetfiddle https://dotnetfiddle.net/vTkmcQ ou usar o código abaixo.
O resultado de uma das muitas corridas dotnetfiddle usando 190 000 iterações foi:
Rendimento pausa: 00: 00: 00,0210611
Enumerable.Empty (): 00: 00: 00,0192563
EmptyEnumerator exemplo: 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; } }
}
}
O uso forma I é a utilização do entrevistador de uma matriz vazia:
public IEnumerator GetEnumerator() {
return new object[0].GetEnumerator();
}
pode também ser utilizada para genérico IEnumerator ou IEnumerable (usar uma matriz do tipo apropriado)
Você pode implementar a interface IEnumerator e IEnumerable, e falso retorno da função 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();
}
}
Eu escrevi assim:
public IEnumerator<T> GetEnumerator()
{
return this.source?.GetEnumerator() ??
Enumerable.Empty<T>().GetEnumerator();
}
Você pode fazer uma NullEnumerator que implementa a interface IEnumerator. Você pode apenas passar uma instância fora do NullEnumerator.
aqui é um exemplo de um EmptyEnumerator
Encontrado esta pergunta procurando a maneira mais simples de obter um enumerador vazio. Depois de ver a resposta comparar o desempenho eu decidi usar a solução de classe enumerador vazio, mas a minha é mais compacto do que os outros exemplos, e é um tipo genérico, e também fornece uma instância padrão para que você não tem que criar novas instâncias todos os tempo, o que deve melhorar ainda mais o desempenho.
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() { }
}