更新记录
本文迁移自Panda666原博客,原发布时间:2021年6月28日。
可枚举类型,可以简单的理解为:
有一个类,类中有挺多的数据,用一种统一的方式把他们列举出来。
在.NET中满足以下任意条件的都是可枚举类型:
Implements System.Collections.IEnumerable
Implements System.Collections.Generic.IEnumerable
Has a public parameterless method named GetEnumerator that returns an enumerator
这三条看起来非常的简单。第一条实现了IEnumerable接口就行了。第二条实现了IEnumerable接口就行了。第三条就更简洁了,实现了GetEnumerator方法就行了。所以可以发现,可枚举类型实现起来非常的方便。
上面不是说了吗,用一种统一的方式把数据列举出来。
那么问题来了,为什么需要用统一的方式取出数据?
因为方便取出数据,一百个类有一百个取出数据的方式,维护起来就很难受了。
所以统一数据的取出方式,是非常有必要的。
说了这么多,是什么,为什么,来看看怎么用。我们先来看看,上面说到的2个接口。
System.Collections.IEnumerable接口的定义
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
}
System.Collections.Generic.IEnumerable接口 的定义
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
注意点:
可以看到2个接口大同小异,都有GetEnumerator方法,但要注意的是2个方法的返回类型是不同的。
GetEnumerator从字面意思也能看出来,表示获得Enumerator。
那么什么是Enumerator?Enumerator表示枚举器。
我们的数据统一数据的取出方式全部都靠枚举器帮我们代劳,而不是直接定义在本类中,当然你也可以这样做,但不建议。接下来现在我们来定义几个可枚举类型。
继承自IEnumerable的可枚举类型
public class PandaClass1 : IEnumerable
{
public IEnumerator GetEnumerator()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
继承自IEnumerable的可枚举类型
public class PandaClass2 : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
直接实现GetEnumerator方法的可枚举类型
public class PandaClass3
{
public IEnumerator GetEnumerator()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
直接实现GetEnumerator方法的可枚举类型
public class PandaClass4
{
public IEnumerator<int> GetEnumerator()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
到这里我们已经搞定可枚举类型了,怎么进行枚举?
Enumerator在英文中有列举出人或物的意思。所以是列举器的意思?不急,我们来对比看看程序中什么是枚举器。
在.NET中满足以下任意条件的都是枚举器:
1、Implements System.Collections.IEnumerator
2、Implements System.Collections.Generic.IEnumerator
3、Has a public parameterless method named MoveNext and property called Current
第三条中的实现MoveNext方法和Current属性倒是挺好理解的,只要实现了这2个成员就叫枚举器。
比如下面这类就叫枚举器
public class Enumerator
{
public IteratorVariableType Current { get {...} }
public bool MoveNext() {...}
}
那么第一二条中的接口是什么,不慌,我们先来看看这2个接口的定义。
IEnumerator接口的定义
namespace System.Collections
{
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
}
IEnumerator接口的定义
namespace System.Collections.Generic
{
public interface IEnumerator<out T> : IEnumerator, IDisposable
{
T Current { get; }
}
}
需要注意的点:
IEnumerator接口返回的是object类型。
从接口的定义我们可以发现IEnumerator继承自IEnumerator。
所以IEnumerator是同样包含的有MoveNext()和Reset()方法的。
MoveNext()方法表示移动到下一个元素。
Reset()方法表示重置枚举器。
Current属性用于获得当前的元素值。
直接继承IEnumerator接口实现枚举器
public class PandaEnumerator1 : IEnumerator
{
//内部没有具体实现,所以先抛出异常
public object Current => throw new NotImplementedException();
public bool MoveNext()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
public void Reset()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
直接继承IEnumerator接口实现枚举器
public class PandaEnumerator2 : IEnumerator<int>
{
//内部没有具体实现,所以先抛出异常
public int Current => throw new NotImplementedException();
//内部没有具体实现,所以先抛出异常
object IEnumerator.Current => throw new NotImplementedException();
//用于释放枚举器使用的资源
public void Dispose()
{
throw new NotImplementedException();
}
public bool MoveNext()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
public void Reset()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
直接实现要求的方法实现枚举器
public class PandaEnumerator3
{
//内部没有具体实现,所以先抛出异常
public object Current => throw new NotImplementedException();
public bool MoveNext()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
public void Reset()
{
//内部没有具体实现,所以先抛出异常
throw new NotImplementedException();
}
}
那么问题来了,枚举类型和枚举器都搞定了。怎么把他们组合起来?怎么用来?
我们这里用一个实例来进行演示。我们先定义一个学生类来表示要枚举的数据。
/// <summary>
/// 学生数据
/// </summary>
public class Student
{
/// <summary>
/// 编号
/// </summary>
public string Id { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
}
然后我们定义枚举器
/// <summary>
/// 枚举器
/// </summary>
public class StudentIEnumerator : IEnumerator<Student>
{
/// <summary>
/// 用于遍历的学生信息
/// </summary>
private List<Student> InnerStudents { get; set; }
/// <summary>
/// 当前的位置
/// </summary>
private int CurrentPosition { get; set; }
public StudentIEnumerator(List<Student> students)
{
this.InnerStudents = students;
this.CurrentPosition = -1;
}
//获得当前的元素
public Student Current => this.InnerStudents[this.CurrentPosition];
//如果无需兼容老代码,可以无需修改
object IEnumerator.Current => throw new NotImplementedException();
public void Dispose()
{
}
public bool MoveNext()
{
if(this.CurrentPosition < this.InnerStudents.Count-1)
{
++this.CurrentPosition;
return true;
}
return false;
}
public void Reset()
{
this.CurrentPosition = -1;
}
}
最后定义可枚举类型
/// <summary>
/// 可枚举类型
/// </summary>
public class StudentCollecion : IEnumerable<Student>
{
/// <summary>
/// 学生数据
/// </summary>
public List<Student> Students { get; set; }
public StudentCollecion()
{
//初始化List
this.Students = new List<Student>();
//虚构用于测试的学生信息
this.Students.Add(new Student() {
Id = "666",
Name = "Panda",
Age = 18
});
this.Students.Add(new Student()
{
Id = "888",
Name = "Donkey",
Age = 18
});
this.Students.Add(new Student()
{
Id = "999",
Name = "Dog",
Age = 18
});
this.Students.Add(new Student()
{
Id = "777",
Name = "Pig",
Age = 18
});
this.Students.Add(new Student()
{
Id = "333",
Name = "Monkey",
Age = 18
});
}
/// <summary>
/// 获得枚举器
/// 返回我们自定义的配套枚举器
/// </summary>
/// <returns></returns>
public IEnumerator<Student> GetEnumerator()
{
return new StudentIEnumerator(this.Students);
}
//如果无需兼容老代码可以不用修改
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
那么问题来了,怎么使用可枚举类型?
方式一:使用foreach。这是最常用的方式。
StudentCollecion students = new StudentCollecion();
foreach (var student in students)
{
Console.WriteLine(student.Id);
}
注意:foreach只是方式二的语法糖。如果枚举器实现了IDisposable接口,foreach会自动调用dispose方法。
方式二:直接使用迭代器
StudentCollecion students = new StudentCollecion();
//获得实例的枚举器
IEnumerator<Student> enumerator = students.GetEnumerator();
//手动操作
while (enumerator.MoveNext())
{
//获得元素值
Console.WriteLine(enumerator.Current.Name);
}
//重置一下枚举器
enumerator.Reset();
实例完整源代码
using System;
using System.Collections;
using System.Collections.Generic;
namespace Panda666comTest
{
/// <summary>
/// 枚举器
/// </summary>
public class StudentIEnumerator : IEnumerator<Student>
{
/// <summary>
/// 用于遍历的学生信息
/// </summary>
private List<Student> InnerStudents { get; set; }
/// <summary>
/// 当前的位置
/// </summary>
private int CurrentPosition { get; set; }
public StudentIEnumerator(List<Student> students)
{
this.InnerStudents = students;
this.CurrentPosition = -1;
}
//获得当前的元素
public Student Current => this.InnerStudents[this.CurrentPosition];
//如果无需兼容老代码,可以无需修改
object IEnumerator.Current => throw new NotImplementedException();
public void Dispose()
{
}
public bool MoveNext()
{
if (this.CurrentPosition < this.InnerStudents.Count - 1)
{
++this.CurrentPosition;
return true;
}
return false;
}
public void Reset()
{
this.CurrentPosition = -1;
}
}
/// <summary>
/// 学生数据
/// </summary>
public class Student
{
/// <summary>
/// 编号
/// </summary>
public string Id { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
}
/// <summary>
/// 可枚举类型
/// </summary>
public class StudentCollecion : IEnumerable<Student>
{
/// <summary>
/// 学生数据
/// </summary>
public List<Student> Students { get; set; }
public StudentCollecion()
{
//初始化List
this.Students = new List<Student>();
//虚构用于测试的学生信息
this.Students.Add(new Student()
{
Id = "666",
Name = "Panda",
Age = 18
});
this.Students.Add(new Student()
{
Id = "888",
Name = "Donkey",
Age = 18
});
this.Students.Add(new Student()
{
Id = "999",
Name = "Dog",
Age = 18
});
this.Students.Add(new Student()
{
Id = "777",
Name = "Pig",
Age = 18
});
this.Students.Add(new Student()
{
Id = "333",
Name = "Monkey",
Age = 18
});
}
/// <summary>
/// 获得枚举器
/// 返回我们自定义的配套枚举器
/// </summary>
/// <returns></returns>
public IEnumerator<Student> GetEnumerator()
{
return new StudentIEnumerator(this.Students);
}
//如果无需兼容老代码可以不用修改
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
class Program
{
static void Main(string[] args)
{
//实例化可枚举类型
StudentCollecion students = new StudentCollecion();
//方式一:使用foreach来遍历可枚举类型
foreach (var student in students)
{
Console.WriteLine(student.Id);
}
//方式二:使用手动调用枚举器
//获得实例的枚举器
IEnumerator<Student> enumerator = students.GetEnumerator();
//手动操作
while (enumerator.MoveNext())
{
//获得元素值
Console.WriteLine(enumerator.Current.Name);
}
//重置一下枚举器
enumerator.Reset();
enumerator.Dispose();
//wait
Console.WriteLine("Success");
Console.ReadKey();
}
}
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章