C# `foreach` 行为 - 说明?

标签 c# foreach

我读过 Eric 的文章 here关于 foreach 枚举以及 foreach 可以工作的不同场景

为了防止旧的 C# 版本进行装箱,C# 团队启用了 foreach 的 duck typing 以在非 Ienumerable 集合上运行。(一个公共(public) GetEnumerator 返回具有 public MoveNextCurrent 属性就足够了(.

所以,Eric 写了一个 sample :

class MyIntegers : IEnumerable
{
  public class MyEnumerator : IEnumerator
  {
    private int index = 0;
    object IEnumerator.Current { return this.Current; }
    int Current { return index * index; }
    public bool MoveNext() 
    { 
      if (index > 10) return false;
      ++index;
      return true;
    }
  }
  public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
  IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}

但我认为它有一些拼写错误 (在 Current 属性实现中缺少 get 访问器) 导致它无法编译(我已经通过电子邮件发送给他)。

无论如何这是一个工作版本:

class MyIntegers : IEnumerable
{
  public class MyEnumerator : IEnumerator
  {
    private int index = 0;
      public void Reset()
      {
          throw new NotImplementedException();
      }

      object IEnumerator.Current {
          get { return this.Current; }
      }
    int Current {
        get { return index*index; }
    }
    public bool MoveNext() 
    { 
      if (index > 10) return false;
      ++index;
      return true;
    }
  }
  public MyEnumerator GetEnumerator() { return new MyEnumerator(); }
  IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
}

好的。

根据 MSDN :

A type C is said to be a collection type if it implements the System.Collections.IEnumerable interface or implements the collection pattern by meeting all of the following criteria:

  • C 包含带有签名 GetEnumerator() 的公共(public)实例方法,它返回结构类型、类类型或接口(interface)类型,在下文中称为 E。

  • E 包含一个带有签名 MoveNext() 和返回类型 bool 的公共(public)实例方法。

  • E 包含一个名为 Current 的公共(public)实例属性,它允许读取当前值。此属性的类型称为集合类型的元素类型。

好的。让我们将文档与 Eric 的示例相匹配

Eric 的样本据说是一个集合类型,因为它确实实现了System.Collections.IEnumerable 接口(interface)(尽管是明确的)。但它不是(!)集合模式,因为第 3 条:MyEnumerator 不是公共(public)实例属性命名当前。

MSDN说:

If the collection expression is of a type that implements the collection pattern (as defined above), the expansion of the foreach statement is:

E enumerator = (collection).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

Otherwise , The collection expression is of a type that implements System.IEnumerable (!), and the expansion of the foreach statement is:

IEnumerator enumerator = 
        ((System.Collections.IEnumerable)(collection)).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}

问题 #1

Eric 的示例似乎既没有实现 collection patternSystem.IEnumerable - 因此它不应匹配上面指定的任何条件。那么为什么我仍然可以通过以下方式迭代它:

 foreach (var element in (new MyIntegers() as IEnumerable ))
             {
                 Console.WriteLine(element);
             }

问题 #2

为什么我必须提到 new MyIntegers() as IEnumerable ?它已经是 Ienumerable (!!) 甚至在那之后,编译器不是已经通过强制转换自己完成了这项工作:

((System.Collections.IEnumerable)(collection)).GetEnumerator() ?

就在这里:

 IEnumerator enumerator = 
            ((System.Collections.IEnumerable)(collection)).GetEnumerator();
    try {
       while (enumerator.MoveNext()) {
       ...

那么为什么它仍然要我提到 Ienumerable 呢?

最佳答案

MyEnumerator does not has the required public methods

是的 - 或者更确切地说,如果 Current 是公开的。所需要的只是它具有:

  • 一个公共(public)的、可读的当前属性
  • 一个没有类型参数返回 bool 的公共(public) MoveNext() 方法

基本上,这里缺少 public 只是另一个错字。事实上,这个例子并没有达到它的目的(防止装箱)。它使用 IEnumerable 实现,因为您正在使用 new MyIntegers() as IEnumerable - 所以表达式类型是 IEnumerable,它只使用整个界面。

您声称它没有实现 IEnumerable(顺便说一句,它是 System.Collections.IEnumerable),但它确实实现了,使用显式接口(interface)实现。

根本不实现 IEnumerable 来测试这类东西是最简单的:

using System;

class BizarreCollection
{
    public Enumerator GetEnumerator()
    {
        return new Enumerator();
    }

    public class Enumerator
    {
        private int index = 0;

        public bool MoveNext()
        {
            if (index == 10)
            {
                return false;
            }
            index++;
            return true;
        }

        public int Current { get { return index; } }
    }
}

class Test
{
    static void Main(string[] args)
    {
        foreach (var item in new BizarreCollection())
        {
            Console.WriteLine(item);
        }
    }
}

现在,如果您将 Current 设为私有(private),它将无法编译。

关于C# `foreach` 行为 - 说明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31342847/

相关文章:

c# - 调试 CLR 挂起

javascript - 从 Object.keys 和 forEach 方法 javascript 访问新对象

javascript - 迭代值时,为什么当值是数字时 typeof(value) 返回 "string"? JavaScript

javascript - 如何一键同时计算多个输入?

java - forEach和RemoveIf一起使用会导致ConcurrentModificationException吗?

php - 如何使用 foreach PHP 循环 3 维数组

c# - 如何使用 ADO.Net C# 将 null 插入到 sql 数据库中

c# - T4 模板生成的文件不包含在项目中

c# - 每个类对象显示不同的用户控件

c# - 将超链接添加到 RadioButtonList