关于C#:返回空的IEnumerator

Return an empty IEnumerator

我有一个接口,它实现了"public IEnumerator GetEnumerator()"方法,因此我可以在foreach语句中使用该接口。

我在几个类中实现了这个接口,在其中一个类中,我想返回一个空的IEnumerator。现在我这样做的方式如下:

1
2
3
4
5
public IEnumerator GetEnumerator()
{
    ArrayList arr = new ArrayList();
    return arr.GetEnumerator();
}

然而,我认为这是一个丑陋的黑客,我禁不住认为有更好的方法返回一个空的IEnumerator。有?


这在C 2中很简单:

1
2
3
4
public IEnumerator GetEnumerator()
{
    yield break;
}

您需要使用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()
    {
    }
}


我很好奇,走得更远了。我做了一个测试,检查这些方法比较yield breakEnumerable.Emtpy和自定义类的效率。

您可以在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
public IEnumerator GetEnumerator() {
    return new object[0].GetEnumerator();
}

它还可以用于泛型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传递一个实例。

下面是一个空分子的例子