IEnumerable和IEnumerator
概述
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的更多相关文章
- 细说 C# 中的 IEnumerable和IEnumerator接口
		
我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...
 - 迭代器学习之一:使用IEnumerable和IEnumerator接口
		
写博客是检验我学习的成果之一以及自我总结的一种方式,以后会经常利用这种方式进行技术交流和自我总结,其中认识不深难免会有错误,但是一直懂得不懂就问,不懂就学的道理! 1.首先看一个简单的列子 , , , ...
 - C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield
		
IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...
 - 关于迭代器中IEnumerable与IEnumerator的区别
		
首先是IEnumerable与IEnumerator的定义: 1.IEnumerable接口允许使用foreach循环,包含GetEnumerator()方法,可以迭代集合中的项. 2.IEnumer ...
 - IEnumerable和IEnumerator 详解 (转)
		
原文链接:http://blog.csdn.net/byondocean/article/details/6871881 参考链接:http://www.cnblogs.com/hsapphire/a ...
 - C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)
		
前言 IEnumerable.IEnumerator到现在为止对这两个接口还是不太理解,不理解但是自己总是想着试着要搞明白,毕竟自己用的少,所以在此先记录一下.以备自己日后可以来翻查,同时也希望园子里 ...
 - [转]那些年我还不懂:IList,ICollection,IEnumerable,IEnumerator,IQueryable
		
1.首先看一个简单的例子 int[] myArray = { 1, 32, 43, 343 }; IEnumerator myie = myArray.GetEnumerator(); myie.Re ...
 - 转载IEnumerable与IEnumerator区别
		
public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { ...
 - IEnumerable、IEnumerator与yield的学习
		
我们知道数组对象可以使用foreach迭代进行遍历,同时我们发现类ArrayList和List也可以使用foreach进行迭代.如果我们自己编写的类也需要使用foreach进行迭代时该怎么办呢? IE ...
 
随机推荐
- Hadoop中客户端和服务器端的方法调用过程
			
1.Java动态代理实例 Java 动态代理一个简单的demo:(用以对比Hadoop中的动态代理) Hello接口: public interface Hello { void sayHello(S ...
 - CF 7C. Line(扩展欧几里德)
			
题目链接 AC了.经典问题,a*x+b*y+c = 0整数点,有些忘记了扩展欧几里德,复习一下. #include <cstdio> #include <iostream> # ...
 - URAL 1117. Hierarchy(DP)
			
题目链接 这破题,根本看不懂题意啊...题意:一棵中序遍历是1 2 3 4 5...的满二叉树,从a a+1 a+2 a+3 b,总共多少步.x到y的距离为中间有多少个点.a > b没注意2Y. ...
 - 省略文字的css
			
在显示一行文字时,如果容器太小,为了显示出省略字符,可以使用 ellipsis { white-space: nowrap; overflow: hidden; text-overflow: elli ...
 - highcharts 时间少8小时问题
			
Highcharts 中默认开启了UTC(世界标准时间),由于中国所在时区为+8,所以经过 Highcharts 的处理后会减去8个小时. 如果不想使用 UTC,有2种方法可供使用: 1.在使用Hig ...
 - Linux多线程实例练习 - pthread_create()
			
Linux多线程实例练习 pthread_create():创建一个线程 int pthread_create(pthread_t *tidp, const pthread_attr_t *attr, ...
 - supervisor、pm2、forever坐下来聊聊
			
supervisor 是开发环境用.或者用nodemon,node-dev 代替了supervisor 和 nodemon,它和coffeescript兼容最好. forever 管理多个站点,每个站 ...
 - (转)深入理解flash重绘
			
深入理解Flash Player重绘 Flash Player 会以SWF内容的帧频速度来刷新需要变化的内容,而这个刷新的过程,我们通常称为“重绘(redraw)”,相信即便是初级的菜鸟也知道,只要使 ...
 - python生成器
			
eg1: >>> (i*i for i in range(5))<generator object <genexpr> at 0x16b8fa0>>&g ...
 - linux笔记四-------用户和组的管理
			
1.linux多用户.多任务操作系统 cat /etc/passwd //查看当前系统用户信息 cat /etc/group //查看当前系统组别信息 2.rbac:基于角色进行权限分配 用 ...