C#学习:集合、迭代、泛型(1)
一、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)的更多相关文章
- java学习——集合框架(泛型,Map)
泛型: ... Map:一次添加一对元素.Collection 一次添加一个元素. Map也称为双列集合,Collection集合称为单列集合. 其实map集合中存储的就是键值对. map集合中必须保 ...
- 在Arrays.asList()引发的问题中进一步学习集合与泛型等内容
前言 最近在网上看到一个问题,情况类似如下(记为问题1): public class Demo { public static void main(String[] args) { System.ou ...
- Java_集合与泛型
Collection 集合,集合是java中提供的一种容器,可以用来存储多个数据.在前面的学习中,我们知道数据多了,可以使用数组存放或者使用ArrayList集合进行存放数据.那么,集合和数组既然都是 ...
- Java集合之泛型的使用
Java集合之泛型的使用 泛型提供了一种轻便灵活的数据操作,数据的安全性相对提高. 泛型提供了对列表元素的约束条件,比如ArrayList有序链表,可存储任意类型的元素. 此处构建一个ArrayLis ...
- Json.Net学习.集合序列化.
只要集合实现了IEnumable接口就可以进行序列化 Json序列化器为序列化及反序列化集合对象提供了良好的支持. ->Serializing 为了序列化一个集合---一个泛型的list,arr ...
- C#非泛型集合和泛型集合的超级详解
C# 泛型集合之非泛型集合类与泛型集合类的对应: ArrayList对应List HashTable对应Dictionary Queue对应Queue Stack对应Stack SortedList对 ...
- 【原】Java学习笔记027 - 泛型
package cn.temptation.test; import java.util.ArrayList; import java.util.Iterator; public class Samp ...
- Java集合与泛型中的几个陷阱,你掉进了几个?
下面我总结了集合.泛型.数组转集合等一些常见的陷进,认真看完,相信你绝对有所收获. 1.List ,List<?> 与 List<Object> 有区别吗? 说实话,我敢保证很 ...
- Java集合与泛型中的陷阱
List,List<Object>区别 List<Integer> t1 = new ArrayList<>(); // 编译通过 List t2 = t1; // ...
- redis学习-集合set常用命令
redis学习-集合set常用命令 1.sadd:添加一个元素到集合中(集合中的元素无序的并且唯一) 2.smembers:查看集合中所有的元素(上图事例) 3.srem:删除结合中指定的元素 4 ...
随机推荐
- hggroup和adress
hggroup通常放在标签内部,不过不做强制要求! adress 通常用于作者的联系信息.比如名字,Email,电话,地址.标签内字体显示斜体. 与 标签的比较:比div简洁,更少的用到id或c ...
- mui 重写back 调用back方法,实现返回就即时刷新页面
需求: 从A-----b页面 B操作完后再返回A ,这时A页面数据变化 1.先是针对安卓机可以点击按钮返回,也可以用本机的返回键返回 监听本机的返回按钮,如果点击就调用写好的自定义刷新事件 (fun ...
- Android 之 悬浮窗
昨天研究Android的悬浮窗,遇到一个问题,研究了一天,总算找到结症了,原因非常坑人..... 问题是这样的,我想要将悬浮窗展现在桌面或其他应用之上,我的开发机子用的是MIUI,结果发现在机子上无论 ...
- 上传头像,界面无跳转,php+js
上传头像,界面无跳转的方式很多,我用的是加个iframe那种.下面直接上代码. html: //route 为后端接口//upload/avatar 为上传的头像的保存地址//imgurl=/uplo ...
- Word分栏
情景描述 Word分栏在小论文的撰写过程中是很常用的技术.但是,我们经常会遇到很难过的情况: 一段文字本来是连续分布的,可是当选择了分两栏 之后,开始部分在左边一栏,中间在右边一栏. ...
- iOS多工程依赖
导入准备工作 1.建立一个Framework & Library->Cocoa Touch Static Library 取名A,并在A里新建一个类用来测试 2.建立一个demo工程B ...
- 在asp.net中如何自己编写highcharts图表导出到自己的服务器上来
1.准备工作:网上下载highcharts导出的关键dll. 1).Svg.dll:因为highcharts的格式其实就是一个xml,采用svg的方式画图: 2).itextsha ...
- 【深度学习系列3】 Mariana CNN并行框架与图像识别
[深度学习系列3] Mariana CNN并行框架与图像识别 本文是腾讯深度学习系列文章的第三篇,聚焦于腾讯深度学习平台Mariana中深度卷积神经网络Deep CNNs的多GPU模型并行和数据并行框 ...
- 【win8技巧】应用商店里面如何搜索应用app
win8应用商店搜索app软件的技巧 1.组合键 WIN+C 打开屏幕最右边磁条 2.点击搜索,输入你想搜的软件名称,里面会列出你已经安装的app或者你点击下面的应用商店选项,就可以搜索互联网上应用商 ...
- jquery script
http://tech.it168.com/a2012/0206/1307/000001307783_all.shtml http://blog.csdn.net/lyatzhongkong/arti ...