我有一个接口,除其他外,实现了一个“public IEnumerator GetEnumerator()”方法,所以我可以在foreach语句中使用该接口。
我在几个类中实现了这个接口,在其中一个类中,我想返回一个空的IEnumerator。现在我按照以下方式做到这一点:
public IEnumerator GetEnumerator()
{
ArrayList arr = new ArrayList();
return arr.GetEnumerator();
}
然而,我认为这是一个丑陋的黑客,我不禁认为有一个更好的方法来返回一个空的IEnumerator。有吗?
答案 0 :(得分:78)
这在C#2中很简单:
public IEnumerator GetEnumerator()
{
yield break;
}
您需要yield break
语句来强制编译器将其视为迭代器块。
这比“自定义”空迭代器效率低,但代码更简单......
答案 1 :(得分:76)
框架中有一个额外的功能:
public static class Enumerable
{
public static IEnumerable<TResult> Empty<TResult>();
}
使用这个你可以写:
var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();
答案 2 :(得分:12)
您可以实现一个实现IEnumerator的虚拟类,并返回它的实例:
class DummyEnumerator : IEnumerator
{
public object Current
{
get
{
throw new InvalidOperationException();
}
}
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
}
答案 3 :(得分:7)
我很好奇,走得更远了。我做了一个测试,检查方法比较yield break
,Enumerable.Emtpy
和自定义类的效率。
您可以在dotnetfiddle https://dotnetfiddle.net/vTkmcQ上查看或使用以下代码。
使用190 000次迭代的许多dotnetfiddle运行之一的结果是:
收益率突破: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; } }
}
}
答案 4 :(得分:5)
我使用的方法是使用空数组的枚举器:
public IEnumerator GetEnumerator() {
return new object[0].GetEnumerator();
}
它也可以用于通用IEnumerator或IEnumerable(使用适当类型的数组)
答案 5 :(得分:1)
你可以实现IEnumerator接口和IEnumerable,并从IEnumerable interfase的MoveNext函数返回false
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();
}
}
答案 6 :(得分:1)
public IEnumerator<T> GetEnumerator()
{
return this.source?.GetEnumerator() ??
Enumerable.Empty<T>().GetEnumerator();
}
答案 7 :(得分:0)
您可以创建一个实现IEnumerator接口的NullEnumerator。你可以从NullEnumerator传递一个实例。
here是EmptyEnumerator的一个示例
答案 8 :(得分:0)
找到这个问题寻找获得空枚举器的最简单方法。在看到比较性能的答案后,我决定使用空的枚举器类解决方案,但是我比其他示例更紧凑,并且是一个通用类型,并且还提供了一个默认实例,因此您不必创建所有的新实例时间,这甚至可以进一步提高绩效。
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() { }
}