C#集合类型大总结
发布时间:2021-12-10 17:00:43 所属栏目:Linux 来源:互联网
导读:ASP站长网 集合是.NET FCL(Framework Class Library)的重要组成部分,我们平常撸C#代码时免不了和集合打交道,FCL提供了丰富易用的集合类型,给我们撸码提供了极
ASP站长网集合是.NET FCL(Framework Class Library)的重要组成部分,我们平常撸C#代码时免不了和集合打交道,FCL提供了丰富易用的集合类型,给我们撸码提供了极大的便利。正是因为这种与生俱来的便利性,使得我们对集合既熟悉又陌生。很多同学可能一直还是停留在使用的层面上,那么今天我们一起来深入学习一下C#语言中的各种集合。 首先我们看一下 FCL 给我们提供的集合接口: mark FCL提供了泛型和非泛型两大类集合类型。因为非泛型集合装箱和拆箱带来的性能开销问题,和泛型集合相比,已经变得越来越鸡肋。所以我们也侧重于泛型集合的分析,但是两者差别不大。 IEnumerable和IEnumerator mark IEnumerable接口是所有集合类型的祖宗接口,其作用相当于Object类型之于其它类型。如果某个类型实现了IEnumerable接口,就意味着它可以被迭代访问,也就可以称之为集合类型(可枚举)。IEnumerable接口定义非常简单,只有一个GetEnumerator()方法用于获取IEnumerator类型的迭代器。 mark 我们可以将迭代器想象成数据库的游标,即序列(集合)中的某个位置,迭代器只能在序列(集合)中向前移动。每调用一次MoveNext(),如果序列(集合)中还有下一个元素,则迭代器移动到下一个元素;Current用于获取序列(集合)中的当前元素;因为迭代器调用一次代码只需要获取一个元素,这意味着我们需要确定访问到了序列(集合)中的哪个位置。Reset()用于重置这种状态,但是基本上不会使用Reset()重置状态。 同一个序列(集合)可能同时存在多个迭代器操作,相当于同时对一个集合进行多个遍历。这种情况下可能会出现迭代彼此交错。那么如何解决呢? 集合类不直接支持 IEnumerator 和 IEnumerator 接口。而是直接支持 IEnumerable接口,其唯一方法是 GetEnumerator,此方法用于返回支持 IEnumerator 的对象。每次调用GetEnumerator()方法时都需要创建一个新的对象,同时迭代器必须保存自身的状态,记录此时已经迭代到哪一个元素。这样迭代器就像是序列中的游标。可以有多个游标,移动其中任何一个都可以枚举集合,与其他迭代器互不影响。 foreach是怎么实现的? for依赖对 Length 属性和索引运算符 ([]) 的支持。借助 Length 属性,C# 编译器可以使用 for 语句迭代数组中的每个元素。for适用于长度固定且始终支持索引运算符的数组,但并不是所有类型集合的元素数量都是已知的。此外,许多集合类(包括 Stack、Queue 和 Dictionary)都不支持按索引检索元素。因此,需要使用一种更为通用的方法来迭代元素集合。假设可以确定第一个、第二个和最后一个元素,那么就没有必要知道元素数量,也没有必要支持按索引检索元素。foreach在这种背景下应运而生。实际上,foreach内部使用迭代器的MoveNext和Current完成元素的遍历。 List<int> list = new List<int>(); List<int>.Enumerator enumerator = list.GetEnumerator(); try { int number; while (enumerator.MoveNext()) { number = enumerator.Current; Console.WriteLine(number); } } finally { enumerator.Dispose(); } 实现自定义集合 我们可以自己实现IEnumerable接口和IEnumerator接口实现自定义集合。 实现自定义可枚举类型: public class MySet : IEnumerable { internal object[] values; public MySet(object[] values) { this.values = values; } public IEnumerator GetEnumerator() { return new MySetIterator(this); } } 手写实现自定义迭代器: public class MySetIterator : IEnumerator { MySet set; /// <summary> /// 保存迭代到的位置 /// </summary> int position; internal MySetIterator(MySet set) { this.set = set; position = -1; } public object Current { get { if(position==-1||position==set.values.Length) { throw new InvalidOperationException(); } int index = position; return set.values[index]; } } public bool MoveNext() { if(position!=set.values.Length) { position++; } return position < set.values.Length; } public void Reset() { position = -1; } } 测试程序: object[] values = { "a", "b", "c", "d", "e" }; MySet mySet = new MySet(values); foreach (var item in mySet) { Console.WriteLine(item); } 这个例子也证明了foreach内部使用迭代器的MoveNext和Current完成遍历。 上面的例子中手写实现迭代器是十分麻烦的,在c#1.0中这是唯一的方式。在c#2.0中,我们可以使用yield语法糖简化迭代器。 public IEnumerator GetEnumerator() { for (int i = 0; i < values.Length; i++) { yield return values[i]; } } IEnumerable和IEnumerator虽然实现简单,只有简单的几个成员,但是却支撑起了C#语言中集合这座高楼大厦。 ICollection和ICollection 从第一张图中,我们可以得知ICollection继承于IEnumerable接口,并且扩展了IEnumerable接口。 mark 主要扩展的功能有: 新增了属性Count,用于记录集合元素个数 支持添加元素和移除元素 支持是否包含某元素 支持清空集合等等 对于任何实现了ICollection接口的集合,我们都可以通过第1条Count属性获取当前集合的元素数,所以这些集合也被称为计数集合。 (编辑:焦作站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |