一、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 = ;
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 = -;
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 - ))
{
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 - ))
{
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 = -;
} public object Current
{
get
{
object currentElement = this.currentElement;
if (dummyObject != currentElement)
{
return currentElement;
}
if (this.index == -)
{
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();
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 :
current = "Hello";
state = ;
return true;
case :
current = "World";
state = ;
return true ;
case :
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 = ; i < ; i++)
yield return names[i];
} public IEnumerator Reverse() /*逆序迭代*/
{
for (int i = ; i >= ; 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的延时加载有什么内存关联,日后再说吧。

C#学习:集合、迭代、泛型(1)的更多相关文章

  1. java学习——集合框架(泛型,Map)

    泛型: ... Map:一次添加一对元素.Collection 一次添加一个元素. Map也称为双列集合,Collection集合称为单列集合. 其实map集合中存储的就是键值对. map集合中必须保 ...

  2. 在Arrays.asList()引发的问题中进一步学习集合与泛型等内容

    前言 最近在网上看到一个问题,情况类似如下(记为问题1): public class Demo { public static void main(String[] args) { System.ou ...

  3. Java_集合与泛型

    Collection 集合,集合是java中提供的一种容器,可以用来存储多个数据.在前面的学习中,我们知道数据多了,可以使用数组存放或者使用ArrayList集合进行存放数据.那么,集合和数组既然都是 ...

  4. Java集合之泛型的使用

    Java集合之泛型的使用 泛型提供了一种轻便灵活的数据操作,数据的安全性相对提高. 泛型提供了对列表元素的约束条件,比如ArrayList有序链表,可存储任意类型的元素. 此处构建一个ArrayLis ...

  5. Json.Net学习.集合序列化.

    只要集合实现了IEnumable接口就可以进行序列化 Json序列化器为序列化及反序列化集合对象提供了良好的支持. ->Serializing 为了序列化一个集合---一个泛型的list,arr ...

  6. C#非泛型集合和泛型集合的超级详解

    C# 泛型集合之非泛型集合类与泛型集合类的对应: ArrayList对应List HashTable对应Dictionary Queue对应Queue Stack对应Stack SortedList对 ...

  7. 【原】Java学习笔记027 - 泛型

    package cn.temptation.test; import java.util.ArrayList; import java.util.Iterator; public class Samp ...

  8. Java集合与泛型中的几个陷阱,你掉进了几个?

    下面我总结了集合.泛型.数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获. 1.List ,List<?> 与 List<Object> 有区别吗? 说实话,我敢保证很 ...

  9. Java集合与泛型中的陷阱

    List,List<Object>区别 List<Integer> t1 = new ArrayList<>(); // 编译通过 List t2 = t1; // ...

  10. redis学习-集合set常用命令

    redis学习-集合set常用命令   1.sadd:添加一个元素到集合中(集合中的元素无序的并且唯一) 2.smembers:查看集合中所有的元素(上图事例) 3.srem:删除结合中指定的元素 4 ...

随机推荐

  1. ecshop二次开发 给商品添加自定义字段

    说起自定义字段,我想很多的朋友像我一样会想起一些开源的CMS(比如Dedecms.Phpcms.帝国)等,他们是可以在后台直接添加自定义字段的. 抱着这种想法我在Ecshop的后台一顿找,不过肿么都木 ...

  2. python AES 加密与解密

    #用aes加密,再用base64 encode def aes_encrypt(data): from Crypto.Cipher import AES import base64 key=setti ...

  3. UGUI-组件

    2015-06-22 UGUI 组件 Canvas 画布 The Canvas component represents the abstract space in which the UI is l ...

  4. nutch 采集到的数据与实际不符

    现象,这个网站我总计能抽取将近500个URL,但实际只抽取了100条 解析:nutch默认从一个页面解析出的链接,只取前 100 个. <property> <name>db. ...

  5. Linux +apache+fastcgi运行c/c++

    在Linux上搭建apache+fastcgi环境,说多了都是泪啊. 花费我几天时间,开源软件虽说好用,但是版本众多,文档缺乏,什么都只能自己摸索. 终于成功运行起来,特此记录. 一. apache ...

  6. 推荐一本书《深入理解PHP内核》

    <深入理解PHP内核> 在线网址:http://www.php-internals.com/

  7. MemSQL Start[c]UP 2.0 - Round 2

    反正晚上睡不着,熬到1点开始做比赛,6个题目只做了2个题目,而且手速还比较慢,待提升空间还很大呢. A题:给定两个0,1串(len<=100000), 但是不是普通的二进制串,而是q进制串,q ...

  8. mac 浏览器 强刷快捷键

    windows 浏览器的刷新快捷键F5,强制刷新Ctrl+F5 Mac 系统下浏览器的刷新快捷键 command+R, 强制刷新快捷键为 command+shift+R

  9. Mpeg-2的同步及时间恢复--STC,PCR,DTS,PTS

    http://blog.csdn.net/hice1226/article/details/6717354 Mpeg-2的同步及时间恢复--STC,PCR,DTS,PTS 摘要:Mpeg-2同步及时间 ...

  10. RxJava开发精要2-为什么是Observables?

    原文出自<RxJava Essentials> 原文作者 : Ivan Morgillo 译文出自 : 开发技术前线 www.devtf.cn 转载声明: 本译文已授权开发者头条享有独家转 ...