返回一个空的 IEnumerator

发布于 2024-08-10 19:28:21 字数 346 浏览 5 评论 0原文

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

我在几个类中实现了这个接口,在其中一个类中,我想返回一个空的 IEnumerator。现在我按以下方式执行此操作:

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

但是我认为这是一个丑陋的黑客,并且我忍不住认为有更好的方法来返回空的 IEnumerator。有没有?

I have an interface that, among other things, implements a "public IEnumerator GetEnumerator()" method, so I can use the interface in a foreach statement.

I implement this interface in several classes and in one of them, I want to return an empty IEnumerator. Right now I do this the following way:

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

However I consider this an ugly hack, and I can't help but think that there is a better way of returning an empty IEnumerator. Is there?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

天邊彩虹 2024-08-17 19:28:21

这在 C# 2 中很简单:

public IEnumerator GetEnumerator()
{
    yield break;
}

您需要使用 yield break 语句来强制编译器将其视为迭代器块。

这将比“自定义”空迭代器效率低,但它的代码更简单......

This is simple in C# 2:

public IEnumerator GetEnumerator()
{
    yield break;
}

You need the yield break statement to force the compiler to treat it as an iterator block.

This will be less efficient than a "custom" empty iterator, but it's simpler code...

肥爪爪 2024-08-17 19:28:21

框架中有一个额外的函数:

public static class Enumerable
{
    public static IEnumerable<TResult> Empty<TResult>();
}

使用它你可以编写:

var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();

There is an extra function in the framework:

public static class Enumerable
{
    public static IEnumerable<TResult> Empty<TResult>();
}

Using this you can write:

var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();
阳光下的泡沫是彩色的 2024-08-17 19:28:21

您可以实现一个实现 IEnumerator 的虚拟类,并返回它的一个实例:

class DummyEnumerator : IEnumerator
{
    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}

You could implement a dummy class that implements IEnumerator, and return an instance of it:

class DummyEnumerator : IEnumerator
{
    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}
執念 2024-08-17 19:28:21

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

您可以在 dotnetfiddle https://dotnetfiddle.net/p5ZkUN 上查看或使用下面的代码。

使用 190 000 次迭代进行的众多 dotnetfiddle 运行之一的结果是:

产量中断:00:00:00.0012208

Enumerable.Empty(): 00:00:00.0007815

EmptyEnumerator 实例:00:00:00.0010226

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();
        
        IEnumerator enumerator1 = YieldBreak();
        sw.Start();
        for (int i = 0; i < Iterations; i++)
        {
            while(enumerator1.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();
        
        Console.WriteLine("Yield break: {0}", sw.Elapsed);
        
        GC.Collect();
        
        IEnumerator enumerator2 = Enumerable.Empty<object>().GetEnumerator();
        sw.Restart();
        for (int i = 0; i < Iterations; i++)
        {
            while(enumerator2.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();
        
        Console.WriteLine("Enumerable.Empty<T>(): {0}", sw.Elapsed);
        
        GC.Collect();
        
        var enumerator3 = new EmptyEnumerator();
        sw.Restart();
        for (int i = 0; i < Iterations; i++)
        {
            while(enumerator3.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; } }
    }
}

I was curious and went a bit further. I made a test that checks how efficient the methods are comparing yield break, Enumerable.Emtpy and custom class.

You can check it out on dotnetfiddle https://dotnetfiddle.net/p5ZkUN or use the code below.

The result of one of the many dotnetfiddle runs using 190 000 iterations was:

Yield break: 00:00:00.0012208

Enumerable.Empty(): 00:00:00.0007815

EmptyEnumerator instance: 00:00:00.0010226

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();
        
        IEnumerator enumerator1 = YieldBreak();
        sw.Start();
        for (int i = 0; i < Iterations; i++)
        {
            while(enumerator1.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();
        
        Console.WriteLine("Yield break: {0}", sw.Elapsed);
        
        GC.Collect();
        
        IEnumerator enumerator2 = Enumerable.Empty<object>().GetEnumerator();
        sw.Restart();
        for (int i = 0; i < Iterations; i++)
        {
            while(enumerator2.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();
        
        Console.WriteLine("Enumerable.Empty<T>(): {0}", sw.Elapsed);
        
        GC.Collect();
        
        var enumerator3 = new EmptyEnumerator();
        sw.Restart();
        for (int i = 0; i < Iterations; i++)
        {
            while(enumerator3.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; } }
    }
}
童话里做英雄 2024-08-17 19:28:21

我使用的方式是使用空数组的枚举器:

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

它也可以用于通用 IEnumerator 或 IEnumerable (使用适当类型的数组)

The way I use is to use the enumerator of an empty array:

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

It can also be used for generic IEnumerator or IEnumerable (use an array of the appropriate type)

初心 2024-08-17 19:28:21

您可以实现 IEnumerator 接口和 IEnumerable,并从 IEnumerable 接口的 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();
    }
}

You can implement IEnumerator interface and IEnumerable, and return false from MoveNext function of 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();
    }
}
手心的温暖 2024-08-17 19:28:21

我是这样写的:

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

I wrote it like this:

public IEnumerator<T> GetEnumerator()
{
    return this.source?.GetEnumerator() ??
            Enumerable.Empty<T>().GetEnumerator();
}
有深☉意 2024-08-17 19:28:21

您可以创建一个实现 IEnumerator 接口的 NullEnumerator。您可以仅从 NullEnumerator 传递一个实例。

这里是一个 EmptyEnumerator 的示例

You can make a NullEnumerator which implements the IEnumerator interface. You can just pass an instance off the NullEnumerator.

here is an example of an EmptyEnumerator

感情洁癖 2024-08-17 19:28:21

发现这个问题正在寻找获取空枚举器的最简单方法。在看到比较性能的答案后,我决定使用空枚举器类解决方案,但我的解决方案比其他示例更紧凑,并且是通用类型,并且还提供了默认实例,因此您不必在所有情况下创建新实例时间,这应该会进一步提高性能。

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() { }
}

Found this question looking for the simplest way to get an empty enumerator. After seeing the answer comparing performance I decided to use the empty enumerator class solution, but mine is more compact than the other examples, and is a generic type, and also provides a default instance so you don't have to create new instances all the time, which should even further improve performance.

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() { }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文