概述

IEnumerable和IEnumerator接口存在的意义:用来实现迭代的功能!

    public interface IEnumerable
{
IEnumerator GetEnumerator();
} public interface IEnumerator
{
object Current { get; } bool MoveNext(); void Reset();
}

迭代的原理

首先来说一下集合实现的原理:对于ArrayList、List<T>等集合,类中有一个私有的数组类型字段,向集合中添加数据时调用Add方法(将数据元素添加到私有数组字段中),而调用类的其他方法时,其实就是对私有数组类型字段的操作。

 public class ArrayList : IList, ICollection, IEnumerable, ICloneable
{
//省略其他代码
private object[] _items; public virtual int Add(object value)
{
if (this._size == this._items.Length)
{
this.EnsureCapacity(this._size + );
}
this._items[this._size] = value;
this._version++;
return this._size++;
}
}

ArrayList

 public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
//省略其他代码
private T[] _items; public void Add(T item)
{
if (this._size == this._items.Length)
{
this.EnsureCapacity(this._size + );
}
this._items[this._size++] = item;
this._version++;
}
}

List

所以对于这些集合来说,本质上集合中的所有元素都是保存在一个私有数组类型的字段中,众所周知,对于ArrayList或者List<T>都可以使用foreach进行迭代,查看集合中的元素。

        static void Main(string[] args)
{
List<string> strs=new List<string>();
strs.Add("DD");
strs.Add("FF");
strs.Add("VV");
strs.Add("WW");
foreach (String str in strs)
{
Console.WriteLine(str);
}
Console.ReadKey();
}

上述这个foreach的迭代的过程是如何实现的呢?foreach为什么可以逐个遍历所以集合中的元素呢?下面我们就用IL反汇编程序来查看上述代码的foreach部分的IL!

  IL_0039:  callvirt   instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
IL_003e: stloc.2
.try
{
IL_003f: br.s IL_0052
IL_0041: ldloca.s CS$5$0000
IL_0043: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::get_Current()
IL_0048: stloc.1
IL_0049: nop
IL_004a: ldloc.1
IL_004b: call void [mscorlib]System.Console::WriteLine(string)
IL_0050: nop
IL_0051: nop
IL_0052: ldloca.s CS$5$0000
IL_0054: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>::MoveNext()
IL_0059: stloc.3
IL_005a: ldloc.3
IL_005b: brtrue.s IL_0041
IL_005d: leave.s IL_006e
} // end .try
finally
{
IL_005f: ldloca.s CS$5$0000
IL_0061: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_0067: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_006c: nop
IL_006d: endfinally
}

看不懂?没关系啦,那么我们就来大致的猜想一下,我们的foreach生成了这么一大坨的IL中居然有Enumerator什么的,难道跟这个有关系吗?恰巧听说IEnumerable和IEnumrator用来实现迭代,恰恰巧我们的ArrayList、List<T>集合都是实现了IEnumerable接口。那么我们就来做的大胆的假设,foreach其实就是执行跟IEnumerable和IEnumrator接口相关的代码,并对保存集合的私有数组字段的索引进行操作,从而来实现迭代的功能。

static void Main(string[] args)
{
List<string> strs=new List<string>();
strs.Add("DD");
strs.Add("FF");
strs.Add("VV");
strs.Add("WW");
IEnumerator<string> items = strs.GetEnumerator();
while (items.MoveNext())
{
Console.WriteLine(items.Current);
}
Console.ReadKey();
}

这段代码也同样实现了对集合元素迭代的功能!在来看一下这个迭代生成的相关IL。

  IL_0038:  callvirt   instance valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<!0> class [mscorlib]System.Collections.Generic.List`1<string>::GetEnumerator()
IL_003d: box valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<string>
IL_0042: stloc.1
IL_0043: br.s IL_0053
IL_0045: nop
IL_0046: ldloc.1
IL_0047: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1<string>::get_Current()
IL_004c: call void [mscorlib]System.Console::WriteLine(string)
IL_0051: nop
IL_0052: nop
IL_0053: ldloc.1
IL_0054: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
IL_0059: stloc.2
IL_005a: ldloc.2
IL_005b: brtrue.s IL_0045

嘿嘿,对比两者生成的IL,目测他们的执行过程中调用的IL指令大体上是一致的(IL指令啥的暂时我也不是很了解),所以我们的猜想应该是正确的,foreach在本质上其实就是通过ArrayList、List<T>中定义的GetEnumerator方法,以及后续的代码实现的!下面就来看看List<T>中是如何定义的。

public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
private T[] _items;
public List<T>.Enumerator GetEnumerator()
{
return new List<T>.Enumerator(this);
}
public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
{
private List<T> list;
private int index;
private int version;
private T current; public T Current
{ get
{
return this.current;
}
} object IEnumerator.Current
{ get
{
if (this.index == || this.index == this.list._size + )
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
}
return this.Current;
}
}
internal Enumerator(List<T> list)
{
this.list = list;
this.index = ;
this.version = list._version;
this.current = default(T);
} public void Dispose()
{
} public bool MoveNext()
{
List<T> list = this.list;
if (this.version == list._version && this.index < list._size)
{
this.current = list._items[this.index];
this.index++;
return true;
}
return this.MoveNextRare();
}
private bool MoveNextRare()
{
if (this.version != this.list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
this.index = this.list._size + ;
this.current = default(T);
return false;
} void IEnumerator.Reset()
{
if (this.version != this.list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
this.index = ;
this.current = default(T);
}
}
}

List<T>

哇哦,原来迭代器是这样的呀!!!!

自定义一个简单的支持迭代的集合

下面我们就来自定义一个支持迭代器的集合

public class UserDefinedCollection<T>:IEnumerable<T>
{
private List<T> list = new List<T>(); public UserDefinedCollection(List<T> param)
{
list = param;
}
public IEnumerator<T> GetEnumerator()
{
return new UserDefinedEnum<T>(list);
} IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
} private sealed class UserDefinedEnum<T>:IEnumerator<T>
{
private List<T> list = null;
private int _CurrentIndex;
private T _CurrentElement; public UserDefinedEnum(List<T> param)
{
this.list = param;
this._CurrentIndex = ;
_CurrentElement = default(T); } public T Current
{
get
{
return _CurrentElement;
}
} object IEnumerator.Current
{
get { return Current; }
} public bool MoveNext()
{
if (this._CurrentIndex < this.list.Count)
{
this._CurrentElement = this.list[this._CurrentIndex++];
return true;
}
return false;
} public void Reset()
{
this._CurrentElement = default(T);
this._CurrentIndex = ;
}
public void Dispose()
{ }
} }

测试自定义集合的迭代功能:

            List<string> strs=new List<string>();
strs.Add("DD");
strs.Add("FF");
strs.Add("VV");
strs.Add("WW");
UserDefinedCollection<String> user = new UserDefinedCollection<string>(strs);
IEnumerator<string> iEnumerator = user.GetEnumerator(); while (iEnumerator.MoveNext())
{
Console.WriteLine(iEnumerator.Current);
} foreach (String str in user)
{
Console.WriteLine(str);
}

扩展:由模型绑定中,绑定泛型类型时学习到泛型相关的知识!

//调用1
    ExtraGenericInterface(typeof(List<User>),typeof(IEnumerable<>))
    //调用2
    ExtraGenericInterface(typeof(IEnumerable<User>),typeof(IEnumerable<>))     public Type ExtraGenericInterface(Type queryType, Type interfaceType)
{
//当前类型queryType是否是泛型
bool b = queryType.IsGenericType;
//返回可以构造当前泛型类型的一个泛型类型,即:由IEnumerable<User>得到 IEnumerable<>
Type tt = queryType.GetGenericTypeDefinition(); bool ttt = tt == interfaceType ? true : false; Func<Type, bool> predicate = t => t.IsGenericType && (t.GetGenericTypeDefinition() == interfaceType);
//Func<Type, bool> predicate = delegate(Type queryType2){return false;};
//如果当前类型是泛型,并且该发行是由interfaceType类型构造的。
if (predicate(queryType))
{
return queryType;
}
else
{
//获取当前类实现的所有类和接口
Type[] types = queryType.GetInterfaces();
//在数组中找,并返回满足 predicate 条件的第一个元素
//也就是在所有父类或实现的接口中找到是泛型并且构造此泛型的类型是interfaceType类型的第一个元素
         //FirstOrDefault<Type>中Type是后面委托predicate的参数类型 
Type tttt = types.FirstOrDefault<Type>(predicate); return queryType.GetInterfaces().FirstOrDefault<Type>(predicate);
} }

  

IEnumerable和IEnumerator的更多相关文章

  1. 细说 C# 中的 IEnumerable和IEnumerator接口

    我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...

  2. 迭代器学习之一:使用IEnumerable和IEnumerator接口

    写博客是检验我学习的成果之一以及自我总结的一种方式,以后会经常利用这种方式进行技术交流和自我总结,其中认识不深难免会有错误,但是一直懂得不懂就问,不懂就学的道理! 1.首先看一个简单的列子 , , , ...

  3. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  4. 关于迭代器中IEnumerable与IEnumerator的区别

    首先是IEnumerable与IEnumerator的定义: 1.IEnumerable接口允许使用foreach循环,包含GetEnumerator()方法,可以迭代集合中的项. 2.IEnumer ...

  5. IEnumerable和IEnumerator 详解 (转)

    原文链接:http://blog.csdn.net/byondocean/article/details/6871881 参考链接:http://www.cnblogs.com/hsapphire/a ...

  6. C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)

    前言 IEnumerable.IEnumerator到现在为止对这两个接口还是不太理解,不理解但是自己总是想着试着要搞明白,毕竟自己用的少,所以在此先记录一下.以备自己日后可以来翻查,同时也希望园子里 ...

  7. [转]那些年我还不懂:IList,ICollection,IEnumerable,IEnumerator,IQueryable

    1.首先看一个简单的例子 int[] myArray = { 1, 32, 43, 343 }; IEnumerator myie = myArray.GetEnumerator(); myie.Re ...

  8. 转载IEnumerable与IEnumerator区别

    public interface IEnumerable {     IEnumerator GetEnumerator(); }   public interface IEnumerator {   ...

  9. IEnumerable、IEnumerator与yield的学习

    我们知道数组对象可以使用foreach迭代进行遍历,同时我们发现类ArrayList和List也可以使用foreach进行迭代.如果我们自己编写的类也需要使用foreach进行迭代时该怎么办呢? IE ...

随机推荐

  1. 比较好用的php函数

    eval(); $b = 2;$c = "+";$d = 3;eval("\$a=$b$c$d;"); //字符串相加,取值 (加减乘除都行) str_repl ...

  2. 深入理解Java:注解(Annotation)基本概念

    转自:http://www.cnblogs.com/peida/archive/2013/04/23/3036035.html 竹子-博客(.NET/Java/Linux/架构/管理/敏捷) 什么是注 ...

  3. why cpp is a shitty language

    // the below is a standard template for any of my writings about c++ cpp_is_a_shitty_language_as { t ...

  4. 基于jquery的图片懒加载js

    function lazyload(option){ var settings={ defObj:null, defHeight: }; settings=$.extend(settings,opti ...

  5. JS调用水晶报表打印翻页按钮事件

    默认的水晶报表打印按钮.翻页按钮太小,并且样式不好调整,考虑自己做一个按钮,然后调用水晶报表的按钮事件. 在实际操作中发现可以在.net按钮的服务器端事件中调用翻页方法: CrystalReportV ...

  6. docker 报Error: docker-engine-selinux conflicts with docker-selinux-1.9.1-25.el7.centos.x86_64

    root@ecshop Deploy]# yum -y install docker-engine-selinux.noarchLoaded plugins: fastestmirrorhttp:// ...

  7. linux top 参数详解

    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.下面详细介绍它的使用方法. top - 01:06:48 up  1:22,   ...

  8. 用css3实现一个带缺口的圆圈(图)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. Hadoop.2.x_HA部署

    一.概念与HA思路 1. 首先Hadoop架构为主从架构(NameNode/DataNode) 2. NameNode管理着文件系统和与维护客户端访问DataNode 3. Hadoop 2.0 之前 ...

  10. print_r、echo、var_dump三者的区别

    这三者均是php里输出的方式. 1.print_r用于输出数组. 2.echo只能用于输出字符串,在没有框架的时候,多用这种方法将结果打印在html页面上. 3.var_dump能输出任何类型的数据, ...