加入收藏 | 设为首页 | 会员中心 | 我要投稿 焦作站长网 (https://www.0391zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

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属性获取当前集合的元素数,所以这些集合也被称为计数集合。

C#集合类型大总结

(编辑:焦作站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读