Вернуть пустой IEnumerator

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

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

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

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

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

 public IEnumerator GetEnumerator() { yield break; } 

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

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

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

 public static class Enumerable { public static IEnumerable Empty(); } 

ты можешь написать

 var NullEnumerable = Enumerable.Empty(); 

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

 class DummyEnumerator : IEnumerator { public object Current { get { throw new InvalidOperationException(); } } public bool MoveNext() { return false; } public void Reset() { } } 

Способ, которым я использую, – использовать счетчик пустого массива:

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

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

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

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

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

Выходной разрыв: 00: 00: 00.0210611

Enumerable.Empty (): 00: 00: 00.0192563

Экземпляр 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().GetEnumerator(); while(enumerator.MoveNext()) { throw new InvalidOperationException("Should not occur"); } } sw.Stop(); Console.WriteLine("Enumerable.Empty(): {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; } } } } в 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().GetEnumerator(); while(enumerator.MoveNext()) { throw new InvalidOperationException("Should not occur"); } } sw.Stop(); Console.WriteLine("Enumerable.Empty(): {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; } } } } в 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().GetEnumerator(); while(enumerator.MoveNext()) { throw new InvalidOperationException("Should not occur"); } } sw.Stop(); Console.WriteLine("Enumerable.Empty(): {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; } } } } в 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().GetEnumerator(); while(enumerator.MoveNext()) { throw new InvalidOperationException("Should not occur"); } } sw.Stop(); Console.WriteLine("Enumerable.Empty(): {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; } } } } 


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

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

здесь приведен пример EmptyEnumerator

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

 public IEnumerator GetEnumerator() { return this.source?.GetEnumerator() ?? Enumerable.Empty().GetEnumerator(); } 

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

 class EmptyEnumerator : IEnumerator { public readonly static EmptyEnumerator value = new EmptyEnumerator(); public T Current => throw new InvalidOperationException(); object IEnumerator.Current => throw new InvalidOperationException(); public void Dispose() { } public bool MoveNext() => false; public void Reset() { } }