呼和浩特网站制作,网站运营核心,网站制作需要多少钱,中铁建设集团门户网登录官网查询C#8.0本质论第十七章–构建自定义集合
17.1更多集合接口
17.1.1IList T 和IDictionary TKey , TValue
这两个接口决定了集合类型是侧重于通过位置索引来获取值#xff0c;还是侧重于通过键来获取值。
实现这两个接口的类都必须提供索引器。
17.1.2IColl…C#8.0本质论第十七章–构建自定义集合
17.1更多集合接口
17.1.1IList T 和IDictionary TKey , TValue
这两个接口决定了集合类型是侧重于通过位置索引来获取值还是侧重于通过键来获取值。
实现这两个接口的类都必须提供索引器。
17.1.2ICollection T
IList T 和IDictionary TKey , TValue 都实现了ICollection T
17.2主要集合类
17.2.1列表集合:List T
List T 类的性质和数组相似关键区别就是随着元素增多这种类会自动扩展。此外列表可通过显示调用TrimToSize()或Capacity来缩小。
**IComparable T 和IComparer T **的区别很细微却很重要。前者说“我知道如何将我自己和我的类型的另一个实例进行比较”后者说“我知道如何比较给定类型的两个实例”。
using System;
using System.Collections.Generic;
// ...
public class Contact
{public string FirstName { get; private set; }public string LastName { get; private set; }public Contact(string firstName, string lastName){this.FirstName firstName;this.LastName lastName;}
}
public class NameComparison : IComparerContact
{public int Compare(Contact? x, Contact? y){if(Object.ReferenceEquals(x, y))return 0;if(x null)return 1;if(y null)return -1;int result StringCompare(x.LastName, y.LastName);if(result 0)result StringCompare(x.FirstName, y.FirstName);return result;}private static int StringCompare(string? x, string? y){if(Object.ReferenceEquals(x, y))return 0;if(x null)return 1;if(y null)return -1;return x.CompareTo(y);}
}17.2.2全序
实现IComparable T 和IComparer T 时必须生成一个全序(total order)必须为任何可能的数据项排列组合提供一致的排序结果。例如上面代码中连实参是null的情况都考虑到了不能任何一个元素为null就返回0否则可能出现两个非null元素等于null但不相等的情况。
17.2.3搜索List T
可以使用Contains()Indexof()LastIndexOf()和BinarySearch()方法。
BinarySearch()要求有序如果没有找到会返回一个负整数。该值的取反结果是“大于被查找元素的下一个元素”的索引。没有更大的则是元素总数。
17.2.4字典集合:Dictionary TKey , TValue
可利用Keys和Values属性只处理字典类中的键或值。返回ICollection T 类型返回的是对原始字典集合中的数据的引用而不是返回拷贝。
17.2.5已排序集合:SortedDictionary TKey , TValue 和SortedList T
元素是按照键排序的
17.2.6栈集合:Stack T
17.2.7队列集合:Queue T
17.2.8链表:LinkedList T
链表集合允许正向和反向遍历。(所以是双向链表)
17.3提供索引器
数组字典和列表都提供了索引器(indexer)以便根据键或索引来获取/设置成员。
interface IPairT
{T First { get; }T Second { get; }T this[PairItem index] { get; }
}public enum PairItem
{First,Second
}public struct PairT : IPairT
{public Pair(T first, T second){First first;Second second;}public T First { get; }public T Second { get; }public T this[PairItem index]{get{switch (index){case PairItem.First:return First;case PairItem.Second:return Second;default:throw new NotImplementedException($The enum { index.ToString() } has not been implemented);}}}
}索引器的声明和属性很相似但不是使用属性名而是使用关键字this后跟方括号中的参数列表。主题也像属性有get和set块。索引可获得多个参数甚至可以重载。
17.4返回null或者空集合
返回数组和集合时允许返回null更好的选择是返回不含任何数据项的集合实例。可避免强迫调用者在便利集合前检查null值。
17.5迭代器
本节讨论如何利用迭代器(iterator)为自定义集合实现自己的IEnumerator T IEnumerable T 和对应的非泛型接口。迭代器使集合的用户能遍历集合的内部结构同时不必了解结构的内部实现。
类要支持用foreach进行迭代就必须实现枚举数(enumerator)模式如第15章所述C#的foreach循环结构被编译器扩展成while循环结构它以从IEnumerable T 接口获取的IEnumerator T 接口为基础。
17.5.1定义迭代器
17.5.2迭代器语法
迭代器提供了迭代器接口(IEnumerable T 和IEnumerator T )的一个快捷实现。
using System.Collections;
using System.Collections.Generic;public class BinaryTreeT : IEnumerableT
{public BinaryTree(T value){Value value;}#region IEnumerableTpublic IEnumeratorT GetEnumerator(){// ...}#endregion IEnumerableTpublic T Value { get; }public PairBinaryTreeT SubItems { get; set; }
}public struct PairT
{public Pair(T first, T second) : this(){First first;Second second;}public T First { get; }public T Second { get; }
}要为GetEnumerator()提供一个实现。
17.5.3从迭代器生成值
迭代器类似于函数但它不是返回(return)一个值而是**生成(yield)**一系列值。
每次迭代器遇到yield return语句都生成一个值之后控制立即回到请求数据项的调用者。当调用者请求下一项时慧紧接着在上一个yield return语句之后执行。
17.5.4迭代器和状态
GetEnumerator()在foreach语句中被首次调用时慧创建一个迭代器对象其状态被初始化为特殊的“起始”状态表示迭代器尚未执行代码所以尚未生成任何值。
只要foreach继续迭代器就会一直持续其状态。循环每一次请求下一个值控制就会一直维持其状态。循环每一次请求下一个值控制就会进入迭代器从上一次离开的位置继续。该位置是根据迭代器对象中存储的状态信息来判断的。foreach终止迭代器的状态就不再保存了。
17.5.5更多的迭代器例子
17.5.6将yield return语句放到循环中
17.5.7取消更多的迭代:yield break
可以使用yield break使MoveNext()返回false使控制立即回到调用者并终止循环。
C#编译器遇到一个迭代器时会根据枚举数模式将代码展开成恰当的CIL在生成的CIL代码中C#编译器首先创建一个嵌套的私有类来实现IEnumerator T 接口以及它的Current熟悉和MoveNext()方法。Current属性返回与迭代器的返回类型对应的一个类型。
using System;
using System.Collections;
using System.Collections.Generic;
// ...[NullableContext(1)][Nullable(0)]public struct Pair[Nullable(2)] T : IPairT, IEnumerableT, IEnumerable{public Pair(T first, T second){First first;Second second;}public T First { get; }public T Second { get; }public T this[PairItem index]{get{PairItem pairItem index;PairItem pairItem2 pairItem;T result;if (pairItem2 ! PairItem.First){if (pairItem2 ! PairItem.Second){throw new NotImplementedException(string.Format(The enum {0} has not been implemented, index.ToString()));}result Second;}else{result First;}return result;}}public IEnumeratorT GetEnumerator(){yield return First;yield return Second;yield break;}IEnumerator IEnumerable.GetEnumerator(){return GetEnumerator();}}
}yield关键字是上下文关键字不是保留关键字。所以可以合法地声明名为yield的局部变量。
事实上C#1.0之后加入的所有关键字都是上下文关键字这是为了防止升级老程序来使用语言的新版本时出问题。
17.5.8在一个类中创建多个迭代器
17.5.9yield语句的要求
只有在返回IEnumerator T 或者IEnumerable T 类型的成员中才能使用yield return语句。
主体包含yield return语句的成员不能包含简单return语句。
yield语句只能在方法用户自定义操作符或者索引器/属性的get访问器方法中出现。成员不可获取任何ref或者out参数。
yield语句不能在匿名方法或Lambda表达式中出现。
yield语句不能在try语句的catch和finally块中出现。此外yield语句在try块中出现的前提是没有catch块。