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?

Foi útil?

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() { }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top