C#foreach的本质是什么、如何实现自定义集合类的foreach遍历

 

探讨关于C#中Foreach的本质

一.为什么数组和集合可以使用foreach遍历

01. 因为数组和集合都实现了IEnumerable接口,该接口中只有一个方法,GetEnumerator(),返回值是IEnumerator 接口类型。

 

 

我们来探讨一下IEnumerable接口、IEnumerator

 IEnumerable接口

只有一个 返回值为IEnumerator 接口类型的GetEnumerator()方法

public System.Collections.IEnumerator GetEnumerator ();

 

IEnumerator接口

有2个 方法、一个属性。

属性

属性
Current

获取集合中位于枚举数当前位置的元素。

 

 

public object Current { get; }

 

方法

方法
MoveNext()

将枚举数推进到集合的下一个元素。

Reset()

将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。

 

 

 

 

 

public bool MoveNext ();

public void Reset ();

了解完foreach原理我们来给自定义的集合类实现foreach的功能,类图设计如下,给班级实现foreach学生的功能:

C#foreach的本质是什么、如何实现自定义集合类的foreach遍历

 

 

 

 

代码如下:

 
using System;
using System.Collections;
using System.Collections.Generic;
 
 
namespace School
{
    /// <summary>
    /// 测试
    /// </summary>
    public class Test
    {

        static void Main()
        {
            ClassOfStudent cof = new ();
            cof.AddStudent(new() { Name = "小明", Age = 15, Height = "165cm" });
            cof.AddStudent(new() { Name = "小s明", Age = 15, Height = "165cm" });
            cof.AddStudent(new() { Name = "王明", Age = 15, Height = "165cm" });
         
            foreach (Student ddfd in cof)
            {

                Console.WriteLine(ddfd);
            }
        }
    }
     
    /// <summary>
    /// 学生
    /// </summary>
    public class Student : Person,IComparable<Student>
    {
       
        private int dage;

        public int Dage { get => dage; set => dage = value; }

        //重载比较方法
        public int CompareTo(Student st)
        {

                return this.Name.CompareTo(st.Name);

        }
        //重载 ToString()方法
        public override string ToString()
        {
            return string.Format($"姓名:{this.Name} 年龄:{this.Age}");
        }

    }

    /// <summary>
    /// 班级
    /// </summary>
    public class ClassOfStudent : Person, IEnumerable
    {

        private string className;
        private int studentNumber;
        private readonly List<Student> students=new();

        public string ClassName { get => className; set => className = value; }

        public int StudentNumber { get => studentNumber; set => studentNumber = value; }
        public Student this[string name]
        {
            get => students.Find(x => x.Name.Contains(name));
        }
        public void AddStudent(Student st)
        {
            students.Add(st);

        }
      
      public  IEnumerator GetEnumerator()
        {
            return (IEnumerator)(new StudentEnumerator(students));
        }
      
    }
    /// <summary>
    /// 枚举学生的类
    /// </summary>
    public class StudentEnumerator : IEnumerator
    {
        public List<Student> students;

        int position = -1;

        public void Reset() => position = -1;
        public StudentEnumerator(List<Student> st)
        {

            students = st;

        }

        public bool MoveNext()
        { position++; return (position < students.Count); }



        object IEnumerator.Current
        {
            get
            {
                return students[position];
            }
        }

    }

    /// <summary>
    /// 人 的共同特征 年龄 姓名 性别等
    /// </summary>
    public abstract class Person
{
    private string name;
    private string height;
    private int age;
    public string Name {

        get => name;
        set
        {
            if (string.IsNullOrEmpty(value)) throw new ArgumentException("不能使用空白名称", "Name");
            name = value;
        }
    }
    public string Height { get => height; set => height = value; }
    public int Age { get => age; set => age = value; }


    public override string ToString()
    {
        return name;


    }

}




    
}

 

上一篇:[Unity]自定义协程CustomYieldInstruction/IEnumerator


下一篇:c# – 如果该序列不为空,则使用IEnumerable序列作为参数调用方法