【问题标题】:How does c# decide which implementation to use for IEnumerable? [duplicate]c# 如何决定 IEnumerable 使用哪个实现? [复制]
【发布时间】:2014-12-14 00:57:06
【问题描述】:

我刚开始学习 C#,我对 MSDN 中的以下代码有点困惑:

IEnumerable<string> strings =
            Enumerable.Repeat("I like programming.", 15);

既然 IEnumerable 是一个接口并且 Enumerable.Repeat() 返回 IEnumerable 类型,那么“字符串”是如何实现的呢?作为 List 或其他容器?

【问题讨论】:

  • 任何可枚举的内部工作都是故意对最终用户隐藏的。通常会有一些内部集合(如列表),但您不应该确定知道。在这种情况下,带有yield 语句的循环就足够了,并且使用更少的内存。

标签: c# collections ienumerable


【解决方案1】:

在这种情况下没有内部集合。事实上,一些可枚举可能是无限的,所以你不能总是期望内部收集。在这个特定的方法中,有一个保存值的字段和一个计算下一步移动次数的循环被调用。如果达到指定的计数,它将停止迭代。当然,这是使用 C# 的迭代器功能实现的,因此实现起来很简单。

【讨论】:

    【解决方案2】:

    这是一个实现细节。 Enumerable&lt;string&gt;.Repeat() 的签名承诺结果是IEnumerable&lt;string&gt;,这就是它返回的内容。

    无论这个值是作为数组、List 还是以任何其他方式生成(后者是这种情况)实现所需的IEnumerable&lt;T&gt;,都无关紧要。不过,请参阅 MSDN 上的备注部分,说:

    这个方法是通过延迟执行来实现的。立即返回值是一个存储执行操作所需的所有信息的对象。在通过直接调用其 GetEnumerator 方法或使用 Visual C# 中的 foreach 或 Visual Basic 中的 For Each 枚举对象之前,不会执行此方法表示的查询。

    如果您对实际实现感兴趣,请参阅the proposed duplicate

    【讨论】:

      【解决方案3】:

      目前无论如何,返回的类型是:

      Enumerable/'<RepeatIterator>d__b5`1'<string>.
      

      您不能将变量定义为 tpye,因为它是匿名类型。匿名类型是通过编译一个名称来实现的,虽然有效的 .NET 不是有效的 C#,但您不可能意外地创建另一个具有相同名称的类型。

      这种特殊的匿名类型是用于实现yield 的类型。同样,当您在 C# 中编写 yield 代码时,这是通过创建实现 IEnumerableIEnumerator 的 .NET 类来编译的。

      你的代码不关心这些,它只关心它得到实现接口的东西。

      考虑到这一点的有效性:

      public IEnumerable<string> SomeStrings()
      {
        if(new Random().Next(0, 2) == 0)
          return new HashSet<string>{"a", "b", "c"};
        else
          return new List<string>{"a", "b", "c"};
      }
      

      调用代码不知道它是否得到HashSet&lt;string&gt;List&lt;string&gt; 并且不在乎。它不会关心版本 2.0 是否实际返回 string[] 或使用 yield

      您可以创建自己的Repeat,如下所示:

      public static IEnumerable<T> Repeat<T>(T element, int count)
      {
        while(count-- > 0)
          yield return element;
      }
      

      我们有一个复杂的地方,如果count 小于零,我们想抛出一个异常。我们不能只做:

      public static IEnumerable<T> Repeat<T>(T element, int count)
      {
        if(count < 0)
          throw new ArgumentOutOfRangeException("count");
        while(count-- != 0)
          yield return element;
      }
      

      这不起作用,因为直到我们真正枚举它(如果我们曾经这样做的话)才会发生抛出,因为yield-定义的枚举在第一次枚举之前不会运行任何代码。因此我们需要;

      public static IEnumerable<T> Repeat<T>(T element, int count)
      {
        if(count < 0)
          throw new ArgumentOutOfRangeException("count");
        return RepeatImpl<T>(element, count);
      }
      private static IEnumerable<T> RepeatImpl<T>(T element, int count)
      {
        while(count-- != 0)
          yield return element;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-10
        相关资源
        最近更新 更多