用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法

文章目录

    • 首先我要澄清继承自IEnumerable不是解决问题的必要条件,实现GetEnumerator方法才是关键
    • 1、使用返回IEnumerator类型的GetEnumerator()方法(无IEnumerable)
      • 1.1、使用IEnumerable有什么好处?
    • 2、使用返回自定义类型的GetEnumerator()方法
    • 3、为什么大部分的答案都表示要继承自IEnumerable接口?
    • 4、为什么要重写IEnumerator()方法

首先我要澄清继承自IEnumerable不是解决问题的必要条件,实现GetEnumerator方法才是关键

可以仔细看看我下面的代码论证过程(没有继承自IEnumerable就可以实现foreach遍历),下面stack overflow的原文。
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第1张图片

参考自stack overflow的文章:https://stackoverflow.com/questions/11296810/how-do-i-implement-ienumerablet
由此我找到了MSDN微软官方文档:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/iteration-statements
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第2张图片

1、使用返回IEnumerator类型的GetEnumerator()方法(无IEnumerable)

using System;
using System.Collections;

namespace ConsoleApp1
{
    public class IEnumerableTest
    {
        static void Main(string[] args)
        {
            MyObjects myObjects = new MyObjects();
            //第一种ArrayList索引方式向集合中添加元素
            myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
            myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };
            //测试输出
            foreach (MyObject x in myObjects)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }
            //第二种数组方式向集合中添加元素
            MyObjects2 myObjects2 = new MyObjects2(new MyObject[2]{
                new MyObject("Hello2",1),
                new MyObject("World",2)
            });
            //测试输出
            foreach (MyObject x in myObjects2)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }
        }
        class MyObject
        {
            public MyObject(string foo, int lName)
            {
                this.Foo = foo;
                this.Bar = lName;
            }
            public MyObject() { }
            public string Foo { get; set; }
            public int Bar { get; set; }
        }

        // 第一种集合的实现方式
        class MyObjects
        {
            ArrayList mylist = new ArrayList();

            //给集合添加元素用的函数(用索引赋值)
            public MyObject this[int index] //索引函数,this在这里代表对当前对象本身的引用
            {
                get { return (MyObject)mylist[index]; }
                set { mylist.Insert(index, value); }
            }
            // 声明 GetEnumerator 接口
            public IEnumerator GetEnumerator()
            {
                return mylist.GetEnumerator();
            }
        }

        // 第二种集合的实现方式
        class MyObjects2
        {
            private MyObject[] objects;

            //给集合添加元素用的函数
            public MyObjects2(MyObject[] objlist) 
            {
                objects = new MyObject[objlist.Length]; //初始化长度
                for (int i = 0; i < objlist.Length; i++)
                    objects[i] = objlist[i];
            }
            // 声明 GetEnumerator 接口
            public IEnumerator GetEnumerator()
            {
                return objects.GetEnumerator();
            }
        }
    }


}

用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第3张图片

1.1、使用IEnumerable有什么好处?

经过微软官方的文档提醒可以继承IEnumerable来对添加元素这里进行优化,如下:
但是这里我一定要声明下,虽然也继承了IEnumerable接口但是这里只是为了优化添加元素,而不是为了foreach的遍历,从上文也可以看出继承自IEnumerable不是必要条件,实现GetEnumerator方法才是关键

using System;
using System.Collections;

namespace ConsoleApp1
{
    public class IEnumerableTest
    {
        static void Main(string[] args)
        {
            //第二种数组方式向集合中添加元素
            /*MyObjects2 myObjects2 = new MyObjects2(new MyObject[2]{
                new MyObject("Hello2",1),
                new MyObject("World",2)
            });*/
            MyObjects2 myObjects2 = new MyObjects2(){
                new MyObject("Hello2",1),
                new MyObject("World",2)
            };
            //测试输出
            foreach (MyObject x in myObjects2)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }
        }
        class MyObject
        {
            public MyObject(string foo, int lName)
            {
                this.Foo = foo;
                this.Bar = lName;
            }
            public MyObject() { }
            public string Foo { get; set; }
            public int Bar { get; set; }
        }

        // 第二种集合的实现方式
        class MyObjects2 : IEnumerable
        {
            private List<MyObject> objects = new List<MyObject>();

            //给集合添加元素用的函数
           /* public MyObjects2(MyObject[] objlist) 
            {
                objects = new MyObject[objlist.Length]; //初始化长度
                for (int i = 0; i < objlist.Length; i++)
                    objects[i] = objlist[i];
            }*/
            public void Add(MyObject x)
            {
                objects.Add(x);
            }
            // 声明 GetEnumerator 接口
            public IEnumerator GetEnumerator()
            {
                return objects.GetEnumerator();
            }
        }
    }


}

2、使用返回自定义类型的GetEnumerator()方法

1、给peopleArray赋值,如下:
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第4张图片
2、进入foreach后执行GetEnumerator方法,来获取集合的元素。进而会进入到我们自定义的PeopleEnum类中 (该类相当于复现了IEnumerator类型)
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第5张图片
3、第一次取元素前会执行PeopleEnum的构造函数
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第6张图片
4、执行到 in 的时候会判断是否移动 position 来获取指针当前索引的值
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第7张图片
5、In返回true才会获取Current属性的值
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第8张图片
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第9张图片
这样就实现了foreach的遍历

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class GetEnumeratorTest
    {
        static void Main()
        {
            Person[] peopleArray = new Person[3]
            {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon"),
            };

            People peopleList = new People(peopleArray);
            foreach (Person p in peopleList)
                Console.WriteLine(p.firstName + " " + p.lastName);
        }
        public class Person //自定义的类
        {
            public Person(string fName, string lName)
            {
                this.firstName = fName;
                this.lastName = lName;
            }

            public string firstName;
            public string lastName;
        }

        // 自定义的集合类,希望能够用foreach遍历People
        public class People
        {
            private Person[] people;
            public People(Person[] pArray) //通过构造器初始化集合
            {
                people = new Person[pArray.Length];

                for (int i = 0; i < pArray.Length; i++)
                {
                    people[i] = pArray[i];
                }
            }

            // 实现GetEnumerator方法
            public PeopleEnum GetEnumerator()
            {
                return new PeopleEnum(people);
            }
            // 使用自带的 IEnumerator
            //public IEnumerator GetEnumerator()
            //{
            //    return mylist.GetEnumerator();
            //}
        }

        // 实现自己的IEnumerator,名为PeopleEnum
        public class PeopleEnum
        {
            public Person[] people;

            int position = -1; //position指向第一个元素前,直到调用MoveNext()

            public PeopleEnum(Person[] list) //构造器初始化
            {
                people = list;
            }

            public bool MoveNext()
            {
                position++;
                return (position < people.Length);
            }

            public void Reset()
            {
                position = -1;
            }

            //object CurrentObj
            //{
            //    get
            //    {
            //        return Current;
            //    }
            //}

            public Person Current 
            {
                get
                {
                    try
                    {
                        return people[position];
                    }
                    catch (IndexOutOfRangeException)
                    {
                        throw new InvalidOperationException();
                    }
                }
            }
        }
    }
}

3、为什么大部分的答案都表示要继承自IEnumerable接口?

因为IEnumerable接口定义了我们需要的public IEnumerator GetEnumerator()方法,如果继承了该接口则必须实现此方法。
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第10张图片

4、为什么要重写IEnumerator()方法

因为要想印证我的猜想foreach遍历访问的对象 不一定 需要实现IEnumerable接口,就必须要脱离IEnumerable接口定义的public IEnumerator GetEnumerator()方法,所以F12后得到了其中IEnumerator的接口定义,我们的目标就是实现类似的功能。好在微软官方有实现的样例,可见第二部分的代码。
用foreach遍历访问的对象需要不一定需要实现IEnumerable接口,但是一定要实现GetEnumerator方法_第11张图片
微软样例:https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator?view=net-7.0

你可能感兴趣的:(.net,c#,开发语言)