Return an empty IEnumerator
我有一个接口,它实现了"public IEnumerator GetEnumerator()"方法,因此我可以在foreach语句中使用该接口。
我在几个类中实现了这个接口,在其中一个类中,我想返回一个空的IEnumerator。现在我这样做的方式如下:
1 2 3 4 5 |
然而,我认为这是一个丑陋的黑客,我禁不住认为有更好的方法返回一个空的IEnumerator。有?
这在C 2中很简单:
1 2 3 4 | public IEnumerator GetEnumerator() { yield break; } |
您需要使用
这将比"自定义"空迭代器效率低,但它的代码更简单…
框架中有一个额外的函数:
1 2 3 4 | public static class Enumerable { public static IEnumerable<TResult> Empty<TResult>(); } |
使用这个你可以写:
1 2 | var emptyEnumerable = Enumerable.Empty<int>(); var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator(); |
可以实现实现IEnumerator的虚拟类,并返回其实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class DummyEnumerator : IEnumerator { public object Current { get { throw new InvalidOperationException(); } } public bool MoveNext() { return false; } public void Reset() { } } |
我很好奇,走得更远了。我做了一个测试,检查这些方法比较
您可以在dotnefiddle https://dotnefiddle.net/vtkmcq上查看或使用下面的代码。
使用190 000次迭代的许多dotnetfidle运行之一的结果是:
Yield break: 00:00:00.0210611
Enumerable.Empty(): 00:00:00.0192563
EmptyEnumerator instance: 00:00:00.0012966
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 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; } } } } |
我使用的方法是使用空数组的枚举器:
1 2 3 |
它还可以用于泛型IEnumerator或IEnumerable(使用适当类型的数组)
可以实现IEnumerator接口和IEnumerable,并从IEnumerable Interfase的moveNext函数返回false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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(); } } |
找到了这个问题,正在寻找获取空枚举器的最简单方法。在看到比较性能的答案后,我决定使用空枚举器类解决方案,但我的解决方案比其他示例更紧凑,而且是通用类型,还提供了一个默认实例,这样您就不必一直创建新实例,这将进一步提高性能。
1 2 3 4 5 6 7 8 9 | 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() { } } |
我是这样写的:
1 2 3 4 5 | public IEnumerator<T> GetEnumerator() { return this.source?.GetEnumerator() ?? Enumerable.Empty<T>().GetEnumerator(); } |
可以生成实现IEnumerator接口的NullEnumerator。您可以从nullEnumerator传递一个实例。
下面是一个空分子的例子