一、System.Collections名称空间下几个接口表征着集合的功能:
1、IEnumerable:表征着迭代功能
public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { bool MoveNext(); object Current { get; } void Reset(); }
注意,IEnumerator也属于System.Collections空间。
2、其它诸如:ICollection IList IDictionary 都表征着相应的集合功能。它们的代码如下:
public interface ICollection : IEnumerable { void CopyTo(Array array, int index); int Count { [__DynamicallyInvokable] get; } } public interface IList : ICollection, IEnumerable { // Methods int Add(object value); void Clear(); bool Contains(object value); int IndexOf(object value); void Insert(int index, object value); void Remove(object value); void RemoveAt(int index); } public interface IDictionary : ICollection, IEnumerable { // Methods [__DynamicallyInvokable] void Add(object key, object value); [__DynamicallyInvokable] void Clear(); [__DynamicallyInvokable] bool Contains(object key); [__DynamicallyInvokable] IDictionaryEnumerator GetEnumerator(); [__DynamicallyInvokable] void Remove(object key); // Properties bool IsFixedSize { [__DynamicallyInvokable] get; } bool IsReadOnly { [__DynamicallyInvokable] get; } object this[object key] { [__DynamicallyInvokable] get; [__DynamicallyInvokable] set; } ICollection Keys { [__DynamicallyInvokable] get; } ICollection Values { [__DynamicallyInvokable] get; } }
上面说的都是接口,它们内部都是些方法声明而已。具体的类,比如System.Array和System.Collections.ArrayList里面才有具体的方法实现。
System.Collections.ArrayList是System.Object对象的集合。
二、创建自己的集合:
一种方式是我们手动实现集合的所有方法,另一种方式是我们继承System.Collections.CollectionBase类。CollectionBase类已经实现了IEnumerable和ICollection、IList接口。
1、在继续之前,我们先来看看CollectionBase是怎么实现的:
[Serializable, ComVisible(true), __DynamicallyInvokable] public abstract class CollectionBase : IList, ICollection, IEnumerable { // Fields private ArrayList list; // Methods protected CollectionBase(); protected CollectionBase(int capacity); public void Clear(); public IEnumerator GetEnumerator(); protected virtual void OnClear(); protected virtual void OnClearComplete(); protected virtual void OnInsert(int index, object value); protected virtual void OnInsertComplete(int index, object value); protected virtual void OnRemove(int index, object value); protected virtual void OnRemoveComplete(int index, object value); protected virtual void OnSet(int index, object oldValue, object newValue); protected virtual void OnSetComplete(int index, object oldValue, object newValue); protected virtual void OnValidate(object value); public void RemoveAt(int index); void ICollection.CopyTo(Array array, int index); int IList.Add(object value); bool IList.Contains(object value); int IList.IndexOf(object value); void IList.Insert(int index, object value); void IList.Remove(object value); // Properties public int Capacity { get; set; } public int Count { [__DynamicallyInvokable] get; } protected ArrayList InnerList { get; } protected IList List { get; } bool ICollection.IsSynchronized { get; } object ICollection.SyncRoot { get; } bool IList.IsFixedSize { get; } bool IList.IsReadOnly { get; } object IList.this[int index] { get; set; } }
可知,CollectionBase是基于ArrayList的,GetEnumerator便是对IEnumerable接口的实现,它返回了一个IEnumerator,那么这个IEnumerator内部到底做了什么使得ICollection可以迭代(通过foreach)呢,显然,CollectionBase没有做这个工作,这个工作是由ArrayList来做的。具体来说是这样:CollectionBase有一个list数据成员,它不是属性(名称用camel命名法,且没有getter/setter),InnerList也是CollectionBase的属性,这个属性的代码以及GetEnumerator的代码如下:
protected ArrayList InnerList { get { if (this.list == null) { this.list = new ArrayList(); } return this.list; } } public IEnumerator GetEnumerator() { return this.InnerList.GetEnumerator(); }
可以看到,确实是基于ArrayList的。
2、那么,我们来看看ArrayList是怎么实现迭代的:
public class ArrayList : IList, ICollection, IEnumerable, ICloneable { // Fields private const int _defaultCapacity = 4; private object[] _items; private int _size; 。。。 public virtual IEnumerator GetEnumerator() { return new ArrayListEnumeratorSimple(this); } 。。。 private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable { // Fields private object currentElement; private static object dummyObject; private int index; [NonSerialized] private bool isArrayList; private ArrayList list; private int version; // Methods static ArrayListEnumeratorSimple(); internal ArrayListEnumeratorSimple(ArrayList list); public object Clone(); public bool MoveNext(); public void Reset(); // Properties public object Current { get; } } }
可以看到ArrayList自己也没干活,而是把迭代任务交给了自己的一个内部类ArrayListEnumeratorSimple,其中有一个构造的修饰符是internal,意思是这个方法只能在程序集内部调用。该类有一个list成员,这个成员便是当ArrayList的GetEnumerator方法调用时把this,即ArrayList自己传给了ArrayListEnumeratorSimple的构造,从而ArrayListEnumeratorSimple的list也就得到初始化了。具体实现代码如下:
private sealed class ArrayListEnumeratorSimple : IEnumerator, ICloneable { // Fields private object currentElement; private static object dummyObject; private int index; private bool isArrayList; private ArrayList list; private int version; // Methods static ArrayListEnumeratorSimple() { dummyObject = new object(); } internal ArrayListEnumeratorSimple(ArrayList list) { this.list = list; this.index = -1; this.version = list._version; this.isArrayList = list.GetType() == typeof(ArrayList); this.currentElement = dummyObject; } public object Clone(){/*...*/} public bool MoveNext() { if (this.version != this.list._version) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); } if (this.isArrayList) { if (this.index < (this.list._size - 1)) { this.currentElement = this.list._items[++this.index]; return true; } this.currentElement = dummyObject; this.index = this.list._size; return false; } if (this.index < (this.list.Count - 1)) { this.currentElement = this.list[++this.index]; return true; } this.index = this.list.Count; this.currentElement = dummyObject; return false; } public void Reset() { if (this.version != this.list._version) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); } this.currentElement = dummyObject; this.index = -1; } public object Current { get { object currentElement = this.currentElement; if (dummyObject != currentElement) { return currentElement; } if (this.index == -1) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumNotStarted")); } throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumEnded")); } } }
迭代的原理很简单:
MoveNext的工作就是将Index加1,然后将新的Index指向的元素设置为当前元素currentElement,这些执行成功就返回true,否则返回false。
Reset的工作就是将Index置为-1。
Current属性则返回当前元素。
3、一个简单的自定义集合:这个就暂时先略了吧,有了上面的分析,我们通过继承CollectionBase的方法去自定义一个集合就很简单了。
三、foreach循环迭代的原理:参考http://li19910722.blog.163.com/blog/static/136856822201359105740400/
只有当一个集合实现了IEnumerable接口,才能在这个集合对象上进行foreach。由参考链接中的内容可知,其实实不实现IEnumerable接口无所谓,只要你有一个GetEnumerator方法返回一个Enumerator即可。要知道的是,Enumerator的MoveNext在将索引加1的同时,返回了true,如果到达界限则返回false,之所以返回bool值,是为了给foreach的in操作作判断用的,为真则执行循环体,否则退出循环。
但这还不够,我还是觉得有些东西没弄明白,比如迭代的时候可以用yield什么的,这其中的原理又是什么呢?看了这篇文章http://jangmon.blogbus.com/logs/36380490.html,总结一下:
foreach (Person p in persons) { Console.WriteLine(p); } //上面的代码会被C#解析成: IEnumerator enumerator = persons.GetEnumerator(); while (enumerator.MoveNext()) { Person p = (Person) enumerator.Current; Console.WriteLine(p); } //可以看出,这和我们上面的说法互补。
//定义一个HelloCollection public class HelloCollection : IEnumerable { public IEnumerator GetEnumerator() { yield return "Hello"; yield return "World"; } } //开始迭代 static void Main(string[] args) { HelloCollection helloCollection=new HelloCollection (); foreach (string s in helloCollection ) { Console.WriteLine(s); } } //HelloCollection最终转换成的代码 public class HelloCollection : IEnumerable { public IEnumerator GetEnumerator() { Enumertor enumerator = new Enumerator(0); return enumerator; } public class Enumertor : IEnumerator, IDisposable { private int state; private object current; public Enumertor(int state) { this.state = state; } } bool System.Collections .IEnumerator .MoveNext() { switch (state) { case 0: current = "Hello"; state = 1; return true; case 1: current = "World"; state = 2; return true ; case 2: break ; } return false ; } void System.Collections .IEnumerator .Reset() { throw new NotSupportedException(); } object System.Collections .IEnumerator .Current { get { return current; } } void IDisposable.Dispose() { } }
我觉得上篇文章的GetEnumerator方法在返回一个new出的Enumerator的时候,应该将state初始化为0,应该是作者是或者作者所参考的书籍的代码中的一个小错误。
GetEnumerator方法是foreach迭代默认在被迭代集合对象上调用的方法,这是可以定制的:
public class MusicTitles:IEnumerable { string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" }; public IEnumerator GetEnumerator() /*顺序迭代*/ { for (int i = 0; i < 4; i++) yield return names[i]; } public IEnumerator Reverse() /*逆序迭代*/ { for (int i = 3; i >= 0; i--) yield return names[i]; } } static void Main(string[] args) { MusicTitles titles = new MusicTitles(); //这个迭代没有指明,则默认就使用被迭代集合的GetEnumerator方法 foreach (string title in titles) { Console.WriteLine(title); } Console.WriteLine(); //这个指明了迭代所用的方法 foreach (string title in titles.Reverse()) { Console.WriteLine(title); } }
四、一点体会:关于迭代,说了上面一堆,又想起Linq查询,大意是只有访问一个Element时才会作真正的查询,不知道Linq跟foreach有什么关系,日后学到了再说,反正有了一点体会,也不知道对不对:foreach跟for的区别在于,for在遍历一个集合的时候,集合中的元素必须是已经存在于内存的,而foreach由于迭代每一个项都走了MoveNext方法,从而我们可以在真正迭代到某元素的时候再临时地在MoveNext里把这个元素查出来(这取决于MoveNext的具体实现了),这似乎有一点延迟加载的味道,不知道和Linq延时查询以及EF的延时加载有什么内存关联,日后再说吧。