题
我有一个接口,除其他事项外,实现了“公共的IEnumerator的GetEnumerator()”方法,所以可以使用该接口在foreach语句中。
我实现这个接口在几类,并在其中的一个,我想返回一个空的IEnumerator。现在我该做的方式如下:
public IEnumerator GetEnumerator()
{
ArrayList arr = new ArrayList();
return arr.GetEnumerator();
}
不过,我认为这是一个丑陋的黑客攻击,我不禁觉得有返回一个空的IEnumerator一个更好的方式。是否有?
解决方案
这是在C#2简单:
public IEnumerator GetEnumerator()
{
yield break;
}
您需要yield break
语句来强制编译器把它作为一个迭代器块。
这会比“自定义”空迭代效率不高,但它是简单的代码...
其他提示
有在框架的额外功能:
public static class Enumerable
{
public static IEnumerable<TResult> Empty<TResult>();
}
使用这个你可以写:
var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();
您可以实现一个实现了IEnumerator一个哑类,并返回它的一个实例:
class DummyEnumerator : IEnumerator
{
public object Current
{
get
{
throw new InvalidOperationException();
}
}
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
}
我很好奇和去远一点。我做了一个试验,检查的效率如何方法比较yield break
,Enumerable.Emtpy
和自定义类。
可以检查出来上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<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; } }
}
}
我使用的方法是使用一个空数组的枚举:
public IEnumerator GetEnumerator() {
return new object[0].GetEnumerator();
}
它也可用于一般的IEnumerator或IEnumerable的(使用相应类型的数组)
可以实现IEnumerator接口和IEnumerable,和自IEnumerable interfase的MoveNext的功能
返回falseprivate 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();
}
}
我写这样的:
public IEnumerator<T> GetEnumerator()
{
return this.source?.GetEnumerator() ??
Enumerable.Empty<T>().GetEnumerator();
}
可以使它实现IEnumerator接口一个NullEnumerator。可以只通过一个实例关闭NullEnumerator。
此处是EmptyEnumerator的一个例子
发现这个问题找得到一个空的枚举的最简单方法。看到比较性能的回答后,我决定使用空枚举级的解决方案,但我比其他例子更紧凑,并且是一个泛型类型,并且还提供了一个默认实例,因此您不必创建新实例的所有时间,这应当进一步改进性能。
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() { }
}