C#中foreach循环中如何调用GetEnumerator方法?

3

我正在学习迭代器模式,当我遇到样例代码中的逻辑时有些困惑。

class MonthCollection : IEnumerable
        {

          public string[] months = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};

            public IEnumerator GetEnumerator()
            {
                // Generates values from the collection
                foreach (string element in months)
                    yield return element;
            }
        }

        static void Main()
        {

            MonthCollection collection = new MonthCollection();
            // Consumes values generated from collection's GetEnumerator method
            foreach (string n in collection)
                Console.Write(n + " ");
            Console.WriteLine("\n");
        }

在这段代码中,访问数组字符串的常规方式是在foreach语句中调用months变量,如下所示: MonthCollection collection = new MonthCollection();
foreach (string n in collection.months){ }
但我不理解为什么foreach可以直接访问集合而不需要直接调用months变量。 MonthCollection collection = new MonthCollection();
foreach (string n in collection){}
更新1:
我不明白的是,在上述类中如何访问变量months而不是直接调用它。
static void Main()
        {

            MonthCollection collection = new MonthCollection();
            // Consumes values generated from collection's GetEnumerator method
            foreach (string n in collection.months)
                Console.Write(n + " ");
            Console.WriteLine("\n");
        }

不是像这样

 MonthCollection collection = new MonthCollection();
               method
                foreach (string n in collection)
                    Console.Write(n + " ");
                Console.WriteLine("\n");
1个回答

3
foreach循环不需要集合,只需要一个对象,该对象具有返回IEnumeratorGetEnumerator()方法,该方法在您的情况下随IEnumerable实现一起出现。
C#编译器产生剩余的“魔法”——一个隐藏的临时变量用于引用枚举器对象,用于开始枚举的代码、获取每个项的代码以及查看枚举何时结束的代码。
在您的情况下,C#依靠您的实现使用另一部分“魔法”——yield return结构来访问months数组的内容。这种结构使您能够实现GetEnumerator()而无需编写单独的类。该结构被转换为一个带有状态机的类,让您可以从循环的中间“返回”对象,然后在请求迭代器的下一个项时重新启动。
yield return返回的对象不必来自集合。您甚至可以将来自集合的对象与其他对象混合使用,例如:
public IEnumerator GetEnumerator() {
    // Generates values from the collection
    foreach (string element in months) {
        yield return element + " will start soon!";
        yield return element;
        yield return element + " has ended.";
    }
}

foreach 循环通过实现 GetEnumerator() 方法的代码间接访问变量 months


你的意思是说,如果在一个类中发现GetEnumerator()方法,C#编译器会自动调用它,而不需要用户显式地调用变量。例如,如果我在Class A中有一个公共属性,并且想要调用该公共属性,则可以这样调用:new A().Name; 但是没有必要显式地调用GetEnumerator()方法,这一切都由C#编译器完成,对吗? - Lijin Durairaj
@LijinJohn C#编译器要求in右侧的对象必须是IEnumerable的实现,否则会在编译时出错。foreach通过调用GetEnumerator()获取枚举器,因为它知道类型检查已经成功。这是C#中使用库定义的类进行代码生成的一部分“魔法”。 - Sergey Kalinichenko
我的问题是,在这个例子中: MonthCollection collection = new MonthCollection();foreach (string n in collection) Console.Write(n + " "); Console.WriteLine("\n");我没有调用GetEnumerator()方法,我只是调用了对象,那么foreach循环如何访问集合? - Lijin Durairaj
它不需要实现 IEnumerable 就可以在 foreach 中使用。只需具有 public TSomething GetEnumerator(),其中 TSomething 具有 public TElement Current { get; }public bool MoveNext() 即可让 foreach 正常工作。 - user4003407
@dasblinkenlight非常感谢您,您帮助我更好地理解了它。 - Lijin Durairaj
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接