一、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. Sql语句批量更新数据(多表关联)

    最近在项目中遇到一个问题,原来设计的功能是不需要一个特定的字段值depid的,但是新的功能需要根据depid来展现,于是出现了这样一个问题,新增加的数据都有正确的depid,而原来的大量的数据就没有d ...

  2. DIV CSS布局中position属性用法深入探究

    本文向大家描述一下DIV CSS布局中的position属性的用法,position属性主要有四种属性值,任何元素的默认position的属性值均是static,静态.这节课主要讲讲relative( ...

  3. HIVE中内连接和左半连接不一致问题

    一.理论 HIVE中都是按等值连接来统计的,理论上两种写法统计结果应该是一致的: 二.实际情况 但实际使用中发现两种写法会返回的结果,总会有一些差距虽然差别不大,但让人很是困惑. 三.原因 当使用jo ...

  4. 产生一个长度为100的int数组,并向其中随机插入1-100,不能重复

    ]; ArrayList myList=new ArrayList(); Random rnd=new Random(); ) { ,); if(!myList.Contains(num)) myLi ...

  5. Spring核心框架 - AOP的起源及介绍

    一.AOP技术起源 AOP技术的诞生并不算晚,早在1990年开始,来自Xerox Palo Alto Research Lab(即PARC)的研究人员就对面向对象思想的局限性进行了分析.他们研究出了一 ...

  6. Docker容器中运行ASP.NET Core

    在Linux和Windows的Docker容器中运行ASP.NET Core 译者序:其实过去这周我都在研究这方面的内容,结果周末有事没有来得及总结为文章,Scott Hanselman就捷足先登了. ...

  7. 关于c++字符串的while(*temp++)

    首先,上一段代码 static bool reverse_str(const char *str) { const char *temp=str; while(*temp++); temp-=; // ...

  8. HDU4515+计算日期

    模拟! /* 计算过了D天后的日期 之前D天的日期 */ #include<stdio.h> int judge_year( int year ){ ==&&year%!= ...

  9. javascript的族家族史

    JavaScript 实现 完整的 JavaScript 实现是由以下 3 个不同部分组成的:ECMAScript.文档对象模型.浏览器对象模型.这也就是说 cocos2d-js 中 其实我们用的是 ...

  10. asp.net中时间差的问题

    asp.net中时间差的问题 在asp中我们可以用datediff来处理,时间的差,相当的不错,可是在asp.net中C#语言中却没有.可是ASP.net给我们提供了一个TimeSpan,我们可以用它 ...