【问题标题】:Return an empty IEnumerator返回一个空的 IEnumerator
【发布时间】:2010-12-15 10:06:38
【问题描述】:

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

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

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

但是我认为这是一个丑陋的 hack,我不禁认为有更好的方法来返回一个空的 IEnumerator。有吗?

【问题讨论】:

    标签: c# ienumerator


    【解决方案1】:

    这在 C# 2 中很简单:

    public IEnumerator GetEnumerator()
    {
        yield break;
    }
    

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

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

    【讨论】:

    • 在 C# 问题上试图与你竞争真的毫无意义。 :-)
    • @Konamiman:一点也不。还记得您回答然后删除的 LINQ 问题,发现该属性的值是必需的吗?我错过了!
    • 请注意,此解决方案并非在所有情况下都有效 - 您不能将 yield break 与返回其他一些枚举器 if (some case) { yield break; } else { return SomeStaticMethodThatGivesEnumerable();} // does not work 结合使用。在这种情况下,请使用其他解决方案中的 Empty 选项。谢谢大家参与。
    • @PeterKay:但问题不在于如何在有额外逻辑的情况下返回一个空枚举器——而是在 OP 需要做的所有事情时如何返回一个空枚举器。如果问题不同,我的答案也会不同。
    【解决方案2】:

    框架中有一个额外的功能:

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

    使用这个你可以写:

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

    【讨论】:

    • 获取 IEnumerator 而不是 IEnumerable: Enumerable.Empty().GetEnumerator();
    【解决方案3】:

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

    class DummyEnumerator : IEnumerator
    {
        public object Current
        {
            get
            {
                throw new InvalidOperationException();
            }
        }
    
        public bool MoveNext()
        {
            return false;
        }
    
        public void Reset()
        {
        }
    }
    

    【讨论】:

      【解决方案4】:

      我很好奇,走得更远了。我做了一个测试,检查这些方法比较 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; } }
          }
      }
      

      【讨论】:

      • 如何创建一个空的 ArrayList/List 并按照 OP 显示的方式返回其枚举数(丑陋的 hack)。它比你在这里测量的方法慢吗?
      • 您需要移动调用以使前两个测试的枚举器也脱离循环,否则它不是有用的比较。
      【解决方案5】:

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

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

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

      【讨论】:

      • 我有点喜欢这个解决方案。这很简单。这是直截了当的。这是经济的。它很优雅(至少从外观上看)。我想知道使用这样的东西是否有任何缺点。不过,会再次购买。
      【解决方案6】:

      可以实现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();
          }
      }
      

      【讨论】:

      【解决方案7】:

      我是这样写的:

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

      【讨论】:

        【解决方案8】:

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

        here 是 EmptyEnumerator 的示例

        【讨论】:

          【解决方案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() { }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-02
            • 1970-01-01
            • 2014-02-22
            • 1970-01-01
            • 2016-11-27
            • 1970-01-01
            • 2011-03-04
            • 2015-07-12
            相关资源
            最近更新 更多