中国军情
枚举(一文详解枚举器和迭代器)
一文详解枚举器和迭代器nerror="javascript:errorimg.call(this);">一文详解枚举器和迭代器nerror="javascript:errorimg.call(this);">

责编 | 胡巍巍

枚举器

枚举器(enumerator)是一个只读的作用于序列值的只能向前的游标,并且实现了System.Collections.IEnumeratar 或者 System.Collections.Generic.IEnumeratar<T>接口的对象。

class EnumeratorDemo : IEnumerator

public object Current

get { return true; }

public bool MoveNext

return false;

public void Reset

//more code

}

可枚举对象可以是实现了Ienumerable或Ienumerable<T>的对象,也可以是具有名为GetEnumerator方法并且方法返回一个枚举器的对象。同样我们通过代码来看一下怎么定义可枚举对象:

class Enumerable : IEnumerable

public IEnumerator GetEnumerator

IEnumerable<string> myEnumerable = ;

return myEnumerable.GetEnumerator;

}

static void Main(string[] args)

using (var item = "abcdefg".GetEnumerator)

while (item.MoveNext)

var _char = item.Current;

Console.WriteLine(_char);

}

Console.Read;

讲解一下上述代码,字符串是可枚举对象,因此可以通过 GetEnumerator方法获得枚举器,然后使用枚举器的MoveNext防火阀做 while语句的执行条件,MoveNext法在存在下一个值得时候会返回True,当不存在的时候返回False。

![niX5b8.png](https://s2.ax1x.com/2019/09/02/niX5b8.png)

小知识

List<string> strList = new List<string> { "张三", "李四", "王五", "赵六" };

List<string> strList = new List<string>;

strList.Add("李四");

strList.Add("赵六");

迭代器

迭代器简化了对象间的通信,使得不关心序列的类型,而获得序列中的每个元素。C#中迭代器被IEnumerator和IEnumerable和其对应的泛型接口封装。

static void Main(string[] args)

foreach (int item in demo(5))

Console.WriteLine(item);

Console.Read;

static IEnumerable<int> demo(int demoCount)

int data = 0;

for (int i = 0; i < demoCount; i++)

yield return data;

data = data + 1;

}

每当遇到yield时控制权都会回归到调用者那里,但是被调用者的状态还会保持。这个状态的生命周期绑定到了枚举器上,当调用这完成枚举之后状态就被释放。

一文详解枚举器和迭代器nerror="javascript:errorimg.call(this);">

编译器把地带方法转换成私有的,实现了 IEnumerable<T>或者 IEnumerator<T>的类。内部的逻辑被反转并被切分到编译器生成的枚举器类中的 MoveNext方法和 Current属性里。这就意味着当你调用迭代器方法时,实际上时对编译器生成的类进行实例化。

一文详解枚举器和迭代器nerror="javascript:errorimg.call(this);">

迭代器含有一个或多个 yield语句的方法、属性或者索引器。而且必须返回 IEnumerable、IEnumerable<T>、 IEnumerator或者 IEnumerator<T>其中的一个,迭代器会根据返回的接口类型选择不同的语义。下面根据上述描述来看一个例子:

static void Main

foreach(int item in demo)

Console.WriteLine(item);

}

static IEnumerable<int> Foo

yield return 1;

yield return 3;

迭代器中的return

static IEnumerable<int> demo

yield return 1;

yield return 3;

yield return 4;

上述代码中我们使用了 yield break来跳出迭代器,yield break 后面的 yield return 4将不再返回。

但是如果没有 catch 语句块,只有 try..finally 语句块,则 yield return 可以出现在其中。

当我们显示使用枚举器时如果没有释放掉枚举,那么将不会执行 finally 中的代码,为了避免这种情况的出现我们可以使用 using 语句。

class Program

static void Main(string[] args)

foreach (int fib in GetNum(Fibs(12)))

Console.Read;

static IEnumerable<int> Fibs(int count)

int result = 0;

for (int i = 0; i < count; i++)

yield return result;

result = result + i;

}

static IEnumerable<int> GetNum(IEnumerable<int> sequence)

foreach (int x in sequence)

yield return x;

}

上述例子中每个元素在 MoveNext 操作请求的时候才会被计算,也就是说 foreach (int fib in GetNum(Fibs(12)))没执行一次循环每个元素值就会被计算一次。

这里需要注意,一般来说迭代器都会结合 foreach语句一起使用,每次循环完成后都必须显示的或隐式的调用 Dispose方法来释放掉枚举器。

作者简介:朱钢,笔名喵叔,CSDN博客专家,.NET高级开发工程师,7年一线开发经验,参与过电子政务系统和AI客服系统的开发,以及互联网招聘网站的架构设计,目前就职于北京恒创融慧科技发展有限公司,从事企业级安全监控系统的开发。

【END】


顶一下()     踩一下()
打赏

热门推荐

发表评论
0评