C# 6.0本质论(创建自定义集合)

本文详细介绍了C#中的集合类,包括List、Dictionary、Stack、Queue等,探讨了泛型和非泛型集合的特性,以及如何实现自定义集合。同时,文章还讲解了排序、搜索、索引器和迭代器的使用,以及如何实现自定义字典的相等性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

十六、创建自定义集合

16.1 集合接口

在这里插入图片描述

  • IList
    • 按位置索引
  • IDictionary
    • 键值对的无序列表,按键索引
  • 泛型集合在 System.Collections.Generics 命名空间下
  • 非泛型集合在 System.Collections 命名空间下

16.2 集合类

16.2.1 List

16.2.1.1 排序
  • IComparable<T>、IComparable
    • 写在被排序的类中
      • 适合明确排序方式的情况,与类绑定紧密
    • List.Sort()会自动调用
    • 集合类对接口的实现只能有一种实现方式
  • IComparer<T>
    • 与排序方法绑定
      • 适合类有多种排序方式且不确定使用哪一种的情况
    • List.Sort()接收IComparer<T>类型的参数进行排序
    • 一种排序方式新建一个方法的实现类
  • 全序
    • IComparable<T>和IComparer<T>的实现都必须为任何可能的数据项的排列组合提供一致的排序结果
    • 全序条件
      • A==A
      • A>B成立,则B<A也应该成立
  • 比较方式
    • 引用类型存的地址是否相等,==或者Object.ReferenceEquals()
    • 值是否相等
      • 具体的比较规则不同,例如,每个成员是否相等
      • 不相等的返回值,大于与小于返回值的符号应该相反
16.2.1.2 搜索
  • Contains():从头开始
  • IndexOf():从头开始
  • LastIndexOf():从尾开始
  • BinarySearch()
    • 只能针对有序集合
    • 若无查找结果,则返回结果为负数
      • 返回结果按位取反的结果是大于被查找元素的下一个元素的索引,即元素在集合中的排序位置
  • FindAll()
    • 接收泛型委托类型作为参数,Predicate<T>
    • public delegate bool Predicate( T obj )

16.2.2 Dictionary

  • 键唯一
16.2.2.1 基本操作
  • 添加元素
    • Add()
      • 键重复会抛出异常
    • [key]索引操作符
      • 键重复会覆盖
  • 读取元素
    • [key]索引操作符
      • 键不存在时会引发异常
  • KeyValuePair<TKey,TValue>
    • Dictionary<TKey, TValue>中的元素类型
16.2.2.2 自定义字典的相等性
  • 实现IEqualityComparer<T>、IEquatable<T>接口
    • IEqualityComparer<T>
      • 针对比较方法,实现Equals()和GetHashCode()
      • 通过集合的构造方法传入实例参数
    • IEquatable<T>
      • 针对类特有的
  • 相等性比较的要求
    • 对象相等必定散列码相等,但散列码相等对象不一定相等
    • 两次运行程序相同成员的对象的散列码可能不相等

16.2.3 SortedDictionary、SortedList

  • SortedDictionary<TKey,TValue>
    • 可按索引和键读取
    • 按索引读取键或者值需要利用Keys和Values属性得到IList<T>
      • 根据索引在List<T>中查找对应的键或者值

16.2.4 Stack

  • 无法使用初始化构造器,Stack没有Add()方法
  • 出栈入栈
    • Push()
    • Pop()
  • 读取栈顶元素
    • Peek()

16.2.5 Queue

  • 出队入队
    • Enqueue()
    • Dequeue()

16.2.6 LinkedList

  • 支持正反向遍历

16.3 索引器

public enum PairItem
{
	First,
	Second
}

interface IPair<T>
{
	T First { get; }
	T Second { get; }
	T this[PairItem index] { get; }
}

public struct Pair<T> : IPair<T>
{
	public T First { get; }
	public T Second { get; }
	public Pair(T first,T second)
	{
		First = first;
		Second = second;
	}
	public T this[PairItem index]
	{
		get
		{
			switch (index)
			{
				case PairItem.First:
					return First;
				case PairItem.Second:
					return Second;
				default:
					throw new NotImplementedException();
			}
		}
	}
}
  • 参数可以定义为可变参数
  • Item属性
    • CIL代码会将索引器自动转化为一个标识符为Item的特殊属性,该属性能显式接收实参
    • 定义了索引器之后就不能再使用Item标识符,会与索引器冲突
    • [System.Runtime.CompilerServices.IndexerName(string itemName) ] 可以为索引器重命名,但不会进入元数据,无法使用反射获取

16.4 迭代器

16.4.1 迭代器的作用

  • foreach遍历集合必须实现枚举数(enumerator)模式
  • foreach等价代码
Stack<int> stack = new Stack<int>();
Stack.Enumerator enumerator = stack.GetEnumerator();
while(enumerator.MoveNext())
{
	number = enumerator.Current;
	Console.WriteLine(number);
}
  • 迭代器是实现这种模式的一种语法简化形式
    • 只需考虑在GetEnumerator()<T>方法中如何通过 yield return 返回一系列值
    • 编译器会将迭代器转换为状态机,可以记录当前状态和位置

16.4.2 迭代器的实现

  • 实现IEnumerable<T>接口的GetEnumerator()<T>方法,该方法返回IEnumerator<T>类型的对象
    • GetEnumerator()方法就是定义如何遍历集合
    • 根据集合结构将所有值通过 yield return 分别返回
    • 迭代器能知道执行到哪个yield return,并在下一次执行时继续
    • 可以用 yield break 取消迭代
    • IEnumerable<T>继承自IEnumerable,所以也应该实现GetEnumerator()方法
    • 可能会使用递归算法,但层次较深的应避免使用递归
public class CSharpBuiltInTypes :IEnumerable<string>
{
	public IEnumerator<string> GetEnumerator()
	{
		yield return "object";
		yield return "byte";
		yield return "uint";
		...
		yield return "string";
	}
	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}
}
public class Program
{
	static void Main()
	{
		var keywords = new CSharpBuildInTypes();
		foreach(var keyword in keywords)
		{
			Console.WriteLine(keyword);
		}
	}
}

在这里插入图片描述

16.4.3 迭代器的工作原理

  • 编译器会将迭代器转换为enumerator模式
  • CIL中会在集合类内部生成 private sealed 的嵌套类
    • 该类实现IEnumerator<T>接口
    • 作为GetEnumerator()<T>方法的返回对象的类型
      • 包含Current属性
      • 包含MoveNext()方法
  • 根据GetEnumerator()<T>方法中的代码自动生成MoveNext()方法,实现enumerator模式
  • 和手动实现enumerator模式的性能基本一致

16.4.4 在单个类中创建多个迭代器

  • 作用
    • 对不同的迭代顺序的支持
  • 在集合中定义多个类似GetEnumerator()<T>的方法
    • 方法名重新定义
    • 返回类型为IEnumerator<T>类型
  • 在foreach中的调用方式
    • foreach ( T item in 集合实例.Get…Enumerator() )
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值