可以仔细看看我下面的代码论证过程(没有继承自IEnumerable就可以实现foreach遍历),下面stack overflow的原文。
参考自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
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();
}
}
}
}
经过微软官方的文档提醒可以继承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();
}
}
}
}
1、给peopleArray赋值,如下:
2、进入foreach后执行GetEnumerator方法,来获取集合的元素。进而会进入到我们自定义的PeopleEnum类中 (该类相当于复现了IEnumerator类型)
3、第一次取元素前会执行PeopleEnum的构造函数
4、执行到 in 的时候会判断是否移动 position 来获取指针当前索引的值
5、In返回true才会获取Current属性的值
这样就实现了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();
}
}
}
}
}
}
因为IEnumerable接口定义了我们需要的public IEnumerator GetEnumerator()
方法,如果继承了该接口则必须实现此方法。
因为要想印证我的猜想foreach遍历访问的对象 不一定 需要实现IEnumerable接口
,就必须要脱离IEnumerable接口定义的public IEnumerator GetEnumerator()
方法,所以F12后得到了其中IEnumerator
的接口定义,我们的目标就是实现类似的功能。好在微软官方有实现的样例,可见第二部分的代码。
微软样例:https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.ienumerator?view=net-7.0