Вернуть пустой IEnumerator - PullRequest
53 голосов
/ 11 ноября 2009

У меня есть интерфейс, который, помимо прочего, реализует метод public IEnumerator GetEnumerator (), поэтому я могу использовать интерфейс в выражении foreach.

Я реализую этот интерфейс в нескольких классах, и в одном из них я хочу вернуть пустой IEnumerator. Прямо сейчас я делаю это следующим образом:

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

Однако я считаю это уродливым взломом, и я не могу не думать, что есть лучший способ вернуть пустой IEnumerator. Есть ли?

Ответы [ 9 ]

78 голосов
/ 11 ноября 2009

Это просто в C # 2:

public IEnumerator GetEnumerator()
{
    yield break;
}

Вам нужен оператор yield break, чтобы компилятор обрабатывал его как блок итератора.

Это будет менее эффективно, чем "пользовательский" пустой итератор, но это более простой код ...

76 голосов
/ 17 марта 2010

В фреймворке есть дополнительная функция:

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

Используя это, вы можете написать:

var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();
12 голосов
/ 11 ноября 2009

Вы можете реализовать фиктивный класс, который реализует IEnumerator, и вернуть его экземпляр:

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

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}
7 голосов
/ 28 ноября 2016

Мне было любопытно, и я пошел немного дальше. Я сделал тест, который проверяет, насколько эффективны методы сравнения yield break, Enumerable.Emtpy и пользовательского класса.

Вы можете проверить это на dotnetfiddle https://dotnetfiddle.net/vTkmcQ или использовать код ниже.

Результатом одного из многих запусков dotnetfiddle с использованием 190 000 итераций было:

Перерыв доходности: 00: 00: 00.0210611

Enumerable.Empty (): 00: 00: 00.0192563

Пустой экземпляр Enumerator: 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; } }
    }
}
5 голосов
/ 11 ноября 2009

Я использую способ перечисления пустого массива:

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

Может также использоваться для универсального IEnumerator или IEnumerable (используйте массив соответствующего типа)

1 голос
/ 27 ноября 2015

Я написал это так:

public IEnumerator<T> GetEnumerator()
{
    return this.source?.GetEnumerator() ??
            Enumerable.Empty<T>().GetEnumerator();
}
1 голос
/ 11 ноября 2009

Вы можете реализовать интерфейс IEnumerator и IEnumerable и возвращать false из функции MoveNext интерфейса 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();
    }
}
0 голосов
/ 03 декабря 2017

Нашел этот вопрос, ища самый простой способ получить пустой перечислитель. Увидев ответ, сравнивающий производительность, я решил использовать пустое решение класса enumerator, но мой более компактен, чем другие примеры, и является универсальным типом, а также предоставляет экземпляр по умолчанию, поэтому вам не нужно создавать новые экземпляры все время, которое должно еще больше улучшить производительность.

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() { }
}
0 голосов
/ 11 ноября 2009

Вы можете создать NullEnumerator, который реализует интерфейс IEnumerator. Вы можете просто передать экземпляр из NullEnumerator.

здесь является примером EmptyEnumerator

...