• 定义泛型集合的命名空间:System.Collections.Generic


    本章导读

    System.Collections.Generic和System.Collections集合的结构有很多相似之处,不同的是“Generic”提供的都是泛型集合,而“Collections”中的集合并不都支持泛型。

    System.Collections.Generic是在C# 2.0中新加的命名空间,主要用来创建强类型集合,以提高类型安全和操作性能。

    本章的讲解流程如图8-1所示。

    图8-1  System.Collections.Generic的讲解流程

    8.1  System.Collections.Generic简介

    本节主要介绍System.Collections.Generic包含的内容。泛型是C# 2.0中新增的一个命名空间,其设计目的主要是为了保障类型转换的安全,常应用在集合中。

    8.1.1  什么是泛型

    泛型的意义在于,通过参数化类型来实现在同一份代码上对多种数据类型的操作。这种解释比较抽象,简单来讲,泛型就是利用参数化类型将类型抽象化,通常称为“类型多态”。

    泛型使用“< >”将类型参数化,其中被尖括号包装的类型,必须是从System.Object继承的共有成员。泛型使用语法如下:

    List<string> dinosaurs = new List<string>();

    泛型的优点如下:

    (1)更强的类型安全。

    (2)更好的复用,因为类型其实是一个参数。

    (3)更高的效率。

    (4)更清晰的约束。

    8.1.2  System.Collections.Generic概述

    System.Collections.Generic用来管理泛型集合。此命名空间也包含System.Collections中的一些集合类,区别在于Generic提供了哪些集合的泛型版本。另外,System.Collections中某些集合是不具备泛型版本的。虽然Generic包含的是集合的泛型版本,但其基本操作方法与Collections中的集合类似。

    Generic命名空间只有在C# 2.0中才有,而且泛型也是2.0中很关键的类型方式,学习本章的内容必须具备一定的集合知识。

    8.1.3  System.Collections.Generic命名空间内的类组成

    集合是数据操作的关键,而泛型集合提高了操作的安全性。System.Collections.Generic命名空间内的类,负责管理并提供泛型集合的一些常用功能。表8-1列出了泛型集合常用的类及其说明。

    表8-1  System.Collections.Generic命名空间内常用的类及其说明

       

       

    Dictionary

    /值对集合

    LinkedList

    双向链表

    List

    ArrayList的泛型等效类,属于动态数组

    Queue

    队列,先进先出的集合

    SortedDictionary

    可以排序的键/值对集合

    SortedList

    SortedDictionary类似,可排序的键/值对集合

    Stack

    栈,后进先出的集合

    System.Collections.Generic命名空间内的接口也非常重要,是自定义和扩展标准集合的关键。表8-2罗列的是System.Collections.Generic命名空间内的常用接口,本章所介绍的大部分类都继承这些接口。

    表8-2  System.Collections.Generic命名空间内常用的接口及其说明

       

    接口说明

    ICollection

    Generic命名空间中类的基接口

    IComparer

    此接口定义比较对象的方法

    IDictionary

    表示键/值对的泛型集合

    IEnumerable

    定义枚举数

    IEnumerator

    支持在泛型集合上进行简单迭代

    IEqualityComparer

    此接口定义“相等”比较的方法

    IList

    可按照索引单独访问的一组对象

    虽然System.Collections.Generic命名空间包含了多个泛型集合类,但这些类除去泛型使用语法不同外,其他操作方法和属性与System.Collections中的类相似。同时鉴于本书的篇幅限制,所以本章只介绍“Dictionary”,“LinkedList”和“SortedDictionary”类,而“Queue”,“SortedList”和“Stack”类的使用,读者可参考介绍“System.Collections”命名空间的有关章节。泛型集合中还有一个“List”类,其实就是一个泛型的ArrayList,学习List可以参考非泛型集合中对ArrayList的介绍。

    8.2  泛型字典集合:Dictionary类

    Dictionary类又称为字典集合,本节介绍其内部构造及使用语法。在使用该类前,一定要弄清楚什么是字典,以及字典集合具备什么样的优点。

    8.2.1  功能说明

    Dictionary类中保存的是键/值对集合,值可以为任意类型数据,其操作方法和构造都与Hashtable类相似,可以把Dictionary看成是Hashtable类的泛型版本。

    Dictionary类也是通过“键”来检索数据的,这是提高检索速度的关键。Dictionary类默认初始容量为3。在实际应用中,如果可以测算出键/值对的数量,那么最好在定义Dictionary类时,指定其初始容量,这样在进行添加和移除操作时,就减少了大小调整这一环节。

    8.2.2  语法定义

    Dictionary类的语法定义如下:

    [SerializableAttribute]

    [ComVisibleAttribute(false)]

    public class Dictionary<TKey,TValue> : IDictionary<TKey,TValue>, ICollection<KeyValuePair<TKey,TValue>>,

    IEnumerable<KeyValuePair<TKey,TValue>>, IDictionary, ICollection, IEnumerable,

    ISerializable, IDeserializationCallback

    此处需注意ICollection<KeyValuePair<TKey,TValue>>和ICollection的区别:一个是泛型集合接口,一个是非泛型集合接口。

    Dictionary有7种构造方法,其中最常用的是默认不带任何参数的方法,使用语法如下:

    Dictionary<string, string> openWith =new Dictionary<string, string>();

    之所以说泛型是一种类型多态,是因为在定义泛型集合的时候,可以指定任意类型,如下所示的构造方法都正确:

    Dictionary<int, string> dic1 =new Dictionary<int, string>();

    Dictionary<int, int> dic1 = new Dictionary<int, int>();

    Dictionary<int, object> dic1 = new Dictionary<int, object>();

    8.2.3  属性详解

    Dictionary类的属性主要用来获取集合中的元素,表8-3详细列出了这些属性及其说明。

    表8-3  Dictionary类的属性及其说明

        

    属性说明

    Comparer

    判断字典中的键是否相等

    Count

    字典中的键/值对数量

    Item

    字典中指定键对应的值

    Keys

    字典中键的集合

    Values

    字典中值的集合

    8.2.4  方法详解

    Dictionary类的方法用来操作字典中的键/值对,如添加、删除等。表8-4列出的是Dictionary类中常用的方法及其说明。

    表8-4  Dictionary类常用的方法及其说明

    方法名称

    方法说明

    Add

    添加键/值对

    Clear

    清空键/值对

    ContainsKey

    判断字典中是否包含指定的主键

    ContainsValue

    判断字典中是否包含指定的值

    Remove

    移除指定的键/值对

    TryGetValue

    获取字典中的指定值

    从上述方法中可以看出,泛型集合的方法与非泛型集合的方法基本相似。其实两者最主要的区别还是类型的参数化,而不是属性和方法的差异。

    下面的代码演示如何使用Dictionary类中的方法,请仔细阅读注释。

    Dictionary<int, string> dic1 =new Dictionary<int, string>();

    dic1.Add(1, "北京");//添加键/值对

    dic1.Add(2, "上海");

    Response.Write("是否包含键‘2’"+dic1.ContainsKey(2)) ;

    Response.Write("是否包含值‘上海’"+dic1.ContainsValue("上海")) ;

    string myValue = "";

    dic1.TryGetValue(1, out myValue);//获取指定键的值

    8.2.5  典型应用:用Dictionary缓存数据库命令参数

    泛型类的主要优点是类型安全,这主要表现在当从集合中获取数据时,无须再进行显式类型转换,因为其已经用“<>”定义了类型参数。如获取HashTable中保存的命令参数,必须执行以下转换:

    SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];

    而Dictionary泛型类,则可以不使用这种转换,而是直接用如下代码实现上述操作:

    SqlParameter[] cachedParms =parmCache[cacheKey];

    本例的目的是使用Dictionary实现命令参数的缓存,读者可对比“Hashtable”类的实例,了解泛型字典集合与非泛型字典集合在使用上的差异。本例的演示步骤如下。

    *     打开VS2005,新建一个网站,命名为“DictionarySample”。

    *     打开默认生成的Default.aspx页。在视图中添加两个“ListBox”控件。

    *     按F7键切换到页面的代码视图。添加对数据库命名空间和集合命名空间的引用,代码如下:

    using System.Data.SqlClient;

    using System.Collections.Generic;

    *     定义静态全局变量“parmCache”,用来缓存命令参数,定义语法如下:

    //定义缓存参数的泛型字典集合,注意static

    private static Dictionary<string, SqlParameter[]> parmCache = new Dictionary<string, SqlParameter[]>();

         在代码中添加两个方法“CacheParameters”和“GetCachedParameters”,其中CacheParameters用来添加键/值对到泛型字典中,GetCachedParameters用来获取泛型字典表中某键的值。两个方法的代码如下:

    //定义设置字典表参数的方法

    public static void CacheParameters(string cacheKey, params SqlParameter[] commandParas)

    {

        parmCache[cacheKey] = commandParas;

    }

    //定义获取字典表参数的方法

    public static SqlParameter[] GetCachedParameters(string cacheKey)

    {

        SqlParameter[] cachedParms = parmCache[cacheKey];

        if (cachedParms == null)//判断是否为空

            return null;

        return cachedParms;

    }

         在页面的Page_Load事件中,为哈希表设置三个键/值对,并在ListBox1中显示字典表中所有的键。代码如下所示,注意黑体部分,其获取键值的方法与哈希表不同。

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        {

            //设计3个参数集合

            SqlParameter[] parmsA = new SqlParameter[] { new SqlParameter("@tabAid",SqlDbType.Int),

                new SqlParameter("@tabAname",SqlDbType.NVarChar,20) };

            SqlParameter[] parmsB = new SqlParameter[] { new SqlParameter("@tabBid",SqlDbType.Int),

                new SqlParameter("@tabBname",SqlDbType.NVarChar,20), new SqlParameter("@tabBdate",SqlDbType.DateTime) };

            SqlParameter[] parmsC = new SqlParameter[] { new SqlParameter("@tabCid",SqlDbType.Int),

                new SqlParameter("@tabCname",SqlDbType.NVarChar,20) };

            //添加参数集合到哈希表中

            CacheParameters("TableA", parmsA);

            CacheParameters("TableB", parmsB);

            CacheParameters("TableC", parmsC);

    //获取所有键

    Dictionary<string, SqlParameter[]>.KeyCollection mykeys = parmCache.Keys;

    foreach (string strkey in mykeys)

            {//让ListBox1显示所有的键

                ListBox1.Items.Add(strkey);

            }

        }

    }

         本例的功能是当选择ListBox1中的键时,在ListBox2中显示此键对应的值,也就是所设置的参数集合。这需要在ListBox1的“SelectedIndexChanged”事件中完成此功能,代码如下:

    protected void ListBox1_SelectedIndexChanged(object sender, EventArgs e)

    {

        //通过键,获取缓存的参数集合

        SqlParameter[] parms = GetCachedParameters(ListBox1.SelectedValue);

        ListBox2.Items.Clear();//清空列表中的旧数据

        foreach (SqlParameter parm in parms)

        {//遍历参数集合,显示所有的参数

            ListBox2.Items.Add(parm.ParameterName);

        }

    }

    注意:ListBox默认的“AutoPostBack”属性为false,为了实现数据的回传,必须将其设置为true。

         按Ctrl+S组合键保存所有的代码,按F5键运行程序,可以发现ListBox1中罗列的是字典表中的键。当单击ListBox1中的某项时,ListBox2会显示对应的参数列表。

    8.3  双向链表集合:LinkedList类

    LinkedList类管理的是双向链表,本节将介绍其概念、组成及应用。这些链表并不像C++中的那些链表一样,学起来特别抽象。C#中的链表类,对所有基本功能都进行了封装,使其应用更简单。

    8.3.1  功能说明

    链表是C语言时期就存在的一种数据结构,是以链接方式存储的线性表,其中数据保存在线性表的节点中。节点内还有一个域,用来存放后继节点的指针,这种节点内只有一个指针域的被称为单向链表。如果节点内有两个指针域,一个指向前一个节点,一个指向后一个节点,那么这种链表被称为双向链表,本节介绍的LinkedList属于双向链表。

    LinkedList类中的节点,属于“LinkedListNode”类型,由于LinkedList属于双向链表,所以其节点内包含两个指针域,用来获取前一个和后一个节点,在实际应用中,LinkedListNode节点类型包含属性“Next”和“Previous”代替了这两个指针域。

    8.3.2  语法定义

    LinkedList类的语法定义如下:

    [SerializableAttribute]

    [ComVisibleAttribute(false)]

    public class LinkedList<T> : ICollection<T>, IEnumerable<T>,

    ICollection, IEnumerable, ISerializable, IDeserializationCallback

    LinkedList继承了ISerializable和IDeserializationCallback,支持序列化和反序列化。

    LinkedList有三种构造方法,最常用的是不带任何参数的构造方法,其构造语法如下:

    LinkedList<string> mylink1 = new LinkedList<string>();

    8.3.3  属性详解

    LinkedList类的属性主要是获取链表的节点,表8-5详细列出了这些属性及其说明。

    表8-5  LinkedList类的属性及其说明

       

    属性说明

    Count

    双向链表中的节点数

    First

    双向链表中的第一个节点

    Last

    双向链表中的最后一个节点

    8.3.4  方法详解

    LinkedList类的方法用来操作双向链表中的节点,如添加、移除等。表8-6详细列出了这些方法及其说明。

    表8-6  LinkedList类的方法及其说明

    方法名称

    方法说明

    AddAfter

    链表的当前节点后添加新节点

    AddBefore

    链表的当前节点前添加新节点

    AddFirst

    在链表的第一个节点前添加新节点

    AddLast

    在链表的最后一个节点后添加新节点

    Clear

    清空链表中的节点

    Contains

    判断链表是否包含某值

    Find

    查找指定值所在的节点。查找到一个后便结束查找

    FindLast

    查找指定值所在的节点。返回最后一个包含此值的节点

    Remove

    移除链表中指定的节点或值

    RemoveFirst

    移除链表中第一个节点

    RemoveLast

    移除链表中最后一个节点

    下面的代码演示如何使用LinkedList类中的方法,由于方法比较多,请仔细阅读注释。

    LinkedList<string> mylink1 = new LinkedList<string>();//初始化

    mylink1.AddFirst("this ");//添加节点

    mylink1.AddLast("is ");

    mylink1.AddLast(" Beijing");

    LinkedListNode<string> mynode = mylink1.Find("is ");//检索节点

    Response.Write("上个节点的值:" + mynode.Previous.Value+"<br />");//上一个节点

    Response.Write("下个节点的值:" + mynode.Next.Value + "<br />");//下一个节点

    mylink1.RemoveLast();//移除节点

    8.3.5  典型应用:用LinkedList实现记录的翻页

    虽然C# 2.0提供了很多功能强大的翻页控件库,但其速度仍然是个瓶颈。本例不使用任何翻页控件,而是使用LinkedList实现一个记录的翻页,这些记录都比较简单。在实际项目中,也可以考虑使用这种方法,实现复杂数据的翻页处理。

    实例的演示步骤如下所述。

    *     打开VS2005,新建一个网站,命名为“LinkedListSample”。

    *     在“App_Code”目录下,添加一个类“UserInfo.cs”,用来自定义记录类型。代码设计如下:

    using System;

    public class UserInfo

    {

        private string _name;

        private string _city;

        public UserInfo(string name, string city)

        {

            _name = name;

            _city = city;

        }

        public string Name

        {

            set { _name = value; }

            get { return _name; }

        }

        public string City

        {

            set { _city = value; }

            get { return _city; }

        }

    }

    *     按Ctrl+S组合键保存自定义的记录类型。

    *     打开Default.aspx页。在视图中添加两个文本框,用来显示人名和城市;再添加两个按钮,用来实现上、下翻页,同时设计两个按钮的CommandName属性分别为“Next”和“Previous”。

         将两个按钮的“Command”事件指向同一事件“Button1_Command”,代码如下:

    protected void Button1_Command(object sender, CommandEventArgs e)

    {

        UserInfo tmpUser=new UserInfo(txtName.Text.Trim(), txtCity.Text.Trim());

        LinkedListNode<UserInfo> mynode = myData.Find(tmpUser);

        if (e.CommandName == "Next")

        {//下一页

           

            txtName.Text = mynode.Next.Value.Name;

            txtName.Text = mynode.Next.Value.City;

        }

        else

        { //上一页

            txtName.Text = mynode.Previous.Value.Name;

            txtName.Text = mynode.Previous.Value.City;

        }

    }

         引用泛型集合命名空间“System.Collections.Generic”。定义双向链表集合,代码如下:

    private static LinkedList<UserInfo> myData = new LinkedList<UserInfo>();//初始化

         在“Page_Load”事件中,为链表赋值,同时显示当前的人名和城市,代码如下:

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        {

            myData.AddLast(new UserInfo("张三","北京"));//设置数据

            myData.AddLast(new UserInfo("王三", "上海"));

            myData.AddLast(new UserInfo("张明", "广州"));

            txtName.Text = "张三";//设置默认值

            txtCity.Text = "北京";

        }

    }

         按Ctrl+S组合键保存所有的设计,再按F5键运行程序,单击上、下翻页按钮,测试记录的翻页情况。

    8.4  排序泛型字典集合:SortedDictionary类

    SortedDictionary类是一个按键排序的字典集合,本节介绍SortedDictionary的主要功能及其使用方法。在使用时,一定要特别注意Sorted的作用。

    8.4.1  功能说明

    SortedDictionary类是一个可以按键排序的Dictionary类,其功能类似于非泛型集合中的SortedList。在SortedDictionary类中,元素的键值是不允许改变的,系统自动通过键进行排序。

    SortedDictionary类与SortedList类的区别如下。

    — 内存的使用:SortedList类使用的内存比SortedDictionary类少。

    — 填充速度:比较前提是要填充的列表已经排序,并一次性填充到字典中,此时SortedList的速度比SortedDictionary快。

    — 添加和删除的速度:当单个在字典中添加和删除元素时,SortedDictionary具备更快的速度。

    8.4.2  语法定义

    SortedDictionary类的语法定义如下:

    [SerializableAttribute]

    public class SortedDictionary<TKey,TValue> : IDictionary<TKey,TValue>, Icollection <KeyValuePair<TKey,TValue>>,

    IEnumerable<KeyValuePair<TKey,TValue>>, IDictionary, ICollection, IEnumerable

    其中SortedDictionary继承ICollection和IDictionary的泛型接口。

    SortedDictionary有4种构造方法,通常使用不带参数的方法来构造SortedDictionary的实例,使用语法如下:

    SortedDictionary<string, string> myDic =new SortedDictionary<string, string>();

    注意:类型参数可为继承System.Object的任意类型。

    8.4.3  属性详解

    SortedDictionary类的属性主要是获取集合中的键/值对,表8-7详细列出了这些属性及其说明。

    表8-7  SortedDictionary类的属性及其说明

       

    属性说明

    Count

    排序字典中键/值对的数量

    Item

    指定键的值

    Keys

    排序字典中的键的集合

    Values

    排序字典中的值的集合

    8.4.4  方法详解

    SortedDictionary类的方法用来对集合内的元素进行操作,如添加、删除等,表8-8列出的是SortedDictionary类中常用的方法及其说明。

    表8-8  SortedDictionary类常用的方法列表

    方法名称

    方法说明

    Add

    在集合中添加键/值对

    Clear

    清空集合中的键/值对

    ContainsKey

    判断指定的键是否包含在集合中

    ContainsValue

    判断指定的值是否包含在集合中

    Remove

    从集合中移除指定的键/值对

    TryGetValue

    获取与指定键相关联的值

    下面的代码演示如何使用SortedDictionary类中的方法:

    SortedDictionary<int,string> mydic=new SortedDictionary<int,string>();//初始化

    mydic.Add(1,"海淀");                  //添加元素

    mydic.Add(2,"西城");

    if (!mydic.ContainsValue("东城"))     //判断是否包含指定的值

        mydic.Add(3, "东城");              //添加元素

    mydic.Remove(1);                     //移除键为1的元素

    string myvalue = "";

    mydic.TryGetValue(2,out myvalue);    //获取键为2的元素的值

    8.4.5  典型应用:使用SortedDictionary实现ListBox的排序

    本例利用SortedDictionary类的自动排序功能,实现ListBox控件的自动排序。要实现的功能是:当用户添加内容到ListBox中时,后台先将内容添加到SortedDictionary类的实例中,此时内容会自动排序,然后将ListBox的数据源绑定到SortedDictionary类的实例,这样就实现了ListBox控件的排序。

    本实例的演示步骤如下所述。

    *     打开VS2005,新建一个网站,命名为“SortedDictionarySample”。

    *     打开Default.aspx页,在视图中添加一个ListBox,用来显示排序后的数据;两个TextBox,用来允许用户添加键/值对;一个按钮,用来实现用户的添加操作。

    *     按F7键切换到代码视图,添加对泛型集合的命名空间“System.Collections.Generic”的引用。

    *     定义页面的全局变量“mydic”,代码如下:

    static SortedDictionary<string, string> mydic = new SortedDictionary<string, string>();

         在第一次加载页面时,需要显示ListBox中已有的内容,代码如下:

    protected void Page_Load(object sender, EventArgs e)

    {

        if (!IsPostBack)

        {

            mydic.Add("1", "首长");//添加集合中的元素

            mydic.Add("3", "政委");

            ListBox1.DataSource = mydic;//绑定数据源

            ListBox1.DataBind();

        }

    }

         当用户输入键/值对,单击“添加”按钮时,需要实现内容的添加和排序,实现代码如下:

    protected void btnAdd_Click(object sender, EventArgs e)

    {

        try

        {

            mydic.Add(txtKey.Text, txtValue.Text);           //添加元素,自动排序

            ListBox1.DataSource = mydic;                 //绑定数据源

            ListBox1.DataBind();

        }

        catch (Exception ex)

        { Response.Write("要添加的键重复!"); }

    }

         按Ctrl+S组合键保存代码,按F5键运行程序,并在“键”文本框内输入“2”,在“值”文本框内输入“将军”。单击“添加”按钮,可以发现ListBox进行了排序,并重新显示了排序后的数据。

    8.5  小结

    本章介绍的是泛型集合命名空间System.Collections.Generic的内容,其中最主要的是“泛型”的意义。类型安全和约束是泛型集合的两大特点。

    System.Collections.Generic和System.Collections中的很多接口和类都相似,掌握好System.Collections中的接口和类,是学习System.Collections.Generic的前提。

  • 相关阅读:
    关于开发 Web AI 的思考(kendryte K210)
    怪不得我说,这几个月的代码数据都跑哪里去了....
    在 Android 上使用蓝牙作为主机进行一对多从机传输数据的实测,理论 5
    写了一下 micropython 的文件系统单元测试
    mark 自己未来要写一下,蓝牙主机一对多从机和 K210 的网络通信优化过程。
    VUE实现Studio管理后台(五):手风琴式折叠组件(Accordion)
    VUE实现Studio管理后台(四):状态模式实现窗口停靠,灵动、自由
    VUE实现Studio管理后台(三):支持多语言国际化(vue-i18n)
    VUE实现Studio管理后台(二):Slot实现选项卡tab切换效果,可自由填装内容
    VUE实现Studio管理后台(一):鼠标拖放改变窗口大小
  • 原文地址:https://www.cnblogs.com/Fooo/p/1472321.html
Copyright © 2020-2023  润新知