开发中若是想要本身实现一个集合数据接口而且能够用foreach
来遍历该集合,通常须要实现2个类spa
IEnumerable
code
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
复制代码
IEnumerator
cdn
public interface IEnumerator
{
bool MoveNext();
void Reset();
object Current { get; }
}
复制代码
GetEnumerator()
方法做用是须要获得一个IEnumerator接口的实例对象
MoveNext()
方法做用是判断此次是否能够遍历blog
Reset()
重置该迭代器的索引索引
Current
取出当前遍历的数据接口
接下来编写2个简单的类来实现foreach的遍历内存
public class MyList<TEntity> : IEnumerable<TEntity>
{
private readonly int _initLength;
private TEntity[] _list;
public MyList()
{
_list = new TEntity[10];
_initLength = 10;
}
public IEnumerator<TEntity> GetEnumerator()
{
return new MyEnumeratir(_list);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(TEntity entity)
{
if(entity==null)
throw new NullReferenceException("添加的entity 对象为Null");
if (getNullLocation() == -1) ResetSize();
int index = getNullLocation();
_list[index] = entity;
}
private int getNullLocation()
{
for (int i = 0; i < _list.Length; i++)
{
if (_list[i] == null)
{
return i;
}
}
return -1;
}
private void ResetSize()
{
TEntity[] newList = new TEntity[_list.Length+10];
for (int i = 0; i < _list.Length; i++)
{
newList[i] = _list[i];
}
_list = newList;
}
}
public class MyEnumerator<TEntity>:IEnumerator<TEntity>
{
private TEntity[] _list;
private int _index;
private TEntity _current;
public MyEnumerator(TEntity[] list)
{
_index = 1;
_list = list;
}
public bool MoveNext()
{
_index++;
if(_index==_list.Length) return false;
TEntity obj = _list[_index];
if(obj==null) return false;
return true;
}
public void Reset()
{
_index= -1;
}
TEntity IEnumerator<TEntity>.Current
{
get
{
_current = _list[_index];
return _current;
}
}
public Object Current => _current;
}
复制代码
上面的编写的代码简单的实现了集合而且能够随意添加数据,接下来添加一下数据验证这个集合类是否能够正常遍历开发
class Program
{
public static void Main(string[] args) {
MyList<Student> myList = new MyList<Student>();
}
myList.Add(new Student
{
Name = "周杰伦",
Age = 18,
Sex = "男"
});
myList.Add(new Student
{
Name = "张靓颖",
Age = 20,
Sex = "女"
});
myList.Add(new Student
{
Name = "雅典啊",
Age = 1000,
Sex = "女"
});
foreach (var student in myList)
{
Console.WriteLine($"姓名:{student.Name} ----- 年龄:{student.Age} ----- 性别:{student.Sex}");
}
Console.ReadLine();
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
}
复制代码
控制台正常输出了添加的3条Student
的实例对象get
上面的代码中MyEnumerator
编写了很长的代码还须要判断边条条件,若是要投入到实际生产环境当中还须要编写更多的边界条件判断非常麻烦。 不过C#开发团队提供了一个更加简单的语法糖来实现以上的操做那就是 关键字 yield
如今改写一下MyList
中的方法GetEnumerator()
public IEnumerator<TEntity> GetEnumerator() {
//return new MyEnumeratir(_list);
for(int i=0;i<=_list.Length-1;i++)
{
if(_list[i]!=null)
{
yield return _list[i];
}
}
}
复制代码
这样看起来代码干净了很多哈 不过yield
使用起来有几条限制
1.不能将 yield return 语句置于 try-catch 块中。 可将 yield return 语句置于 try-finally 语句的 try 块中。
2.可将 yield break 语句置于 try 块或 catch 块中,但不能将其置于 finally 块中。
3.若是 foreach 主体(在迭代器方法以外)引起异常,则将执行迭代器方法中的 finally 块。
上面的实例是解释了迭代器如何实现,实际开发中你这样写代码得让别人给骂死。
实际场景中yield
也有很多应用的地方例如遍历一个日期 读取文本文件的字符,读取一个内存流