C#开发训练营

第11课:数组与集合(2)

本课继续讨论集合相关类型,包括Dictionary、ConcurrentDictionary类,以及tPair和tPairList类的封装。

Dictionary泛型类

Dictionary泛型类用于处理“键/值”对应的集合,也就是集合中的元素由“键(Key)”和“值(Value)”组成;在使用Dictionary泛型类时,可以指定键和值的类型,如下面的代码演示了Dictionary的基本应用,其中键和值的类型都定义为string类型。

C#
using System;
using System.Collections.Generic;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();
        dict.Add("Earth", "地球");
        dict.Add("Mars", "火星");
        dict.Add("Jupiter", "木星");
        foreach(string key in dict.Keys)
        {
            tWeb.WriteLine(key + " : " + dict[key]);
        }
    }
}

代码执行结果如图1。

图1

使用Dictionary类时,类型结构为<键类型,值类型>。本例中的Add()方法用于添加元素,其中,参数一为键,参数二为值。访问元素时,可以使用Keys属性获取所有键名,然后以键为索引访问元素的值。

同时获取元素的键和值时,可以通过KeyValuePair结构实现,如下面的代码。

C#
using System;
using System.Collections.Generic;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();
        dict.Add("Earth", "地球");
        dict.Add("Mars", "火星");
        dict.Add("Jupiter", "木星");
        foreach (KeyValuePair<string, string> item in dict)
        {
            tWeb.WriteLine(item.Key + " : " + item.Value);
        }
    }
}

代码执行结果与图1相同。

下面是Dictionary泛型类的常用属性。

  • Count属性,返回集合中的元素数量。
  • Keys属性,返回元素的键组成的集合。
  • Values属性,返回元素的值组成的集合。

下面是Dictionary泛型类的常用方法。

  • Add(key,value)方法,向集合添加元素。
  • Clear()方法,清除集合中的所有元素。
  • ContainsKey(key)方法,判断指定的键是否存在,存在时返回true,否则返回false。
  • ContainsValue(value)方法,判断指定的值是否存在,存在时返回true,否则返回false。
  • Remove(key)方法,删除指定键名的元素,元素存在并删除时返回true,否则返回false。
  • TryGetValue(TKey key, out TValue value)方法,尝试获取指定键名的元素的值,参数一指定键名,如果元素存在,则由参数二带出元素的值,方法返回true;如果指定键名的元素不存在,则方法返回false。

此外,还可以使用SortedList或SortedDictionary泛型类处理“键/值”集合,集合中的元素会通过“键”进行排序,对象的操作与Dictionary类型相似。下面的代码演示了SortedList类的基本应用。

C#
using System;
using System.Collections.Generic;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SortedList<int,string> lst = new SortedList<int, string>();
        lst.Add(2, "ccc");
        lst.Add(0, "aaa");
        lst.Add(1, "bbb");
        foreach(KeyValuePair<int,string> pair in lst)
        {
            tWeb.WriteLine(pair.Key + " : " + pair.Value);
        }
    }
}

代码中,在向lst对象添加集合元素时,并没有按“键”顺序操作,但在对象的内部会按“键”排列元素;通过foreach语句访问所有元素时可以看到已按“键”排序的结果,如图2。

图2

ConcurrentDictionary泛型类

应用中,特别是有并发操作的应用中,如果需要维护一个全局的集合,就必须考虑并发操作中的线程安全问题。从.NET Framework 4.0开始,新增了System.Collections.Concurrent命名空间,定义了一系列的线程安全集合类型,本节主要讨论如何使用ConcurrentDictionary类操作线程安全的字典集合。

ConcurrentDictionary类中定义了与Dictionary类相同的成员,如:

  • Count,返回元素的数量。
  • Keys,返回所有“键”组成的集合。
  • Values,返回所有“值”组成的集合。
  • 索引器,可以使用“键”作为索引访问相应的“值”。
  • Clear()方法,清除所有元素。
  • ContainsKey(k)方法,当键k存在是返回true,否则返回false。

下面的代码演示了ConcurrentDictionary类的基本应用。

C#
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ConcurrentDictionary<string, string> dict =
            new ConcurrentDictionary<string, string>();
        dict["key1"] = "value1";
        dict["key2"] = "value2";
        dict.AddOrUpdate("key1", "value3", (k, v) => "value3");
        //
        foreach (KeyValuePair<string, string> pair in dict)
        {
            tWeb.WriteLine(pair.Key + " : " + pair.Value);
        }
    }
}

代码执行结果如图3。

图3

本例,首先创建了一个ConcurrentDictionary对象,其中“键”和“值”都是定义为string类型;然后通过索引器添加了两个元素,分别是“key1/value1”和“key2/value2”。接下来使用了AddOrUpdate()方法,其功能是对于不存在的键会添加新元素,对于已存在的键则按指定的规则更新元素数据,并返回并的数据。

AddOrUpdate()方法用于更新key1键中的数据,方法使用了三个参数,参数一指定添加或修改数据的键名,参数二指定键不存在时添加的新值,参数三使用了委托类型,指定当键存在时的数据更新规则;代码中,直接将key1元素的数据指定为value3,此外,委托中的参数分别表示已存在元素的键和值,如时更新数据与原数据相关时,可以参考如下代码。

C#
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ConcurrentDictionary<string, string> dict =
            new ConcurrentDictionary<string, string>();
        dict["key1"] = "value1";
        dict["key2"] = "value2";
        dict.AddOrUpdate("key1", "value3", (k, v) => v + ",update");
        //
        foreach (KeyValuePair<string, string> pair in dict)
        {
            tWeb.WriteLine(pair.Key + " : " + pair.Value);
        }
    }
}

代码执行结果如图4。

图4

下面再了解一些ConcurrentDictionary类中的常用方法,首先是GetOrAdd()方法,其中,参数一指定“键”,参数二指定“值”;当“键”存在时,返回元素的“值”,如果“键”不存在,则添加新的元素。下面的代码演示了GetOrAdd()方法的应用。

C#
using System;
using System.Collections.Concurrent;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ConcurrentDictionary<string, string> dict =
            new ConcurrentDictionary<string, string>();
        string value = dict.GetOrAdd("key1", "value1");
        tWeb.WriteLine(value);
        value = dict.GetOrAdd("key1", "");
        tWeb.WriteLine(value);
    }
}

本例,第一次调用GetOrAdd()方法时,键key1不存在,会添加“key1/value1”元素到字典;第二次调用GetOrAdd()方法,键key1已存在,会返回其值value1。

ToArray()方法一个KeyValuePair数组,包含了字典中的所有“键/值”对,下面的代码演示了ToArray()方法的应用。

C#
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ConcurrentDictionary<string, string> dict =
            new ConcurrentDictionary<string, string>();
        dict["key1"] = "value1";
        dict["key2"] = "value2";
        dict["key3"] = "value3";
        var arr = dict.ToArray();
        for (int i = 0; i < arr.Length; i++)
        {
            tWeb.WriteLine(arr[i].Key + " : " + arr[i].Value);
        }
    }
}

代码执行结果如图5。

图5

ConcurrentDictionary类中还有一些基本的元素操作方法,如:

  • TryAdd (TKey key, TValue value)方法,成功添加“key/value”元素时返回true,添加失败或key已存在时返回false。
  • TryGetValue (TKey key, out TValue value)方法,当字典中存在键key,则通过输出参数value返回元素的值,方法返回true;如果键key不存在,方法返回false。
  • TryRemove (TKey key, out TValue value)方法,
  • TryUpdate (TKey key, TValue newValue, TValue comparisonValue)方法,如果字典中的key有对应的comparisonValue,则将其修改newValue。成功修改时返回true,否则返回false。

下面的代码演示了TryRemove()方法的应用。

C#
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ConcurrentDictionary<string, string> dict =
            new ConcurrentDictionary<string, string>();
        dict["key1"] = "value1";
        dict["key2"] = "value2";
        string value;
        //
        tWeb.WriteLine(dict.TryRemove("key1",out value));
        tWeb.WriteLine(value);
        //
        tWeb.WriteLine(dict.TryRemove("key3", out value));
        tWeb.WriteLine(value == null);
    }
}

代码执行结果如图6。

图6

代码中首先定义了字典对象并添加了两个元素。第一个输出用于删除键为key1的元素,此元素存在方法会返回true,输出参数会带出删除元素的值value1(第二个输出),第三个输出是删除键key3的结果,由于元素不存在则方法返回false,输出参数带出的结果为null,第四个输出显示了方法带出结果与null值的比较结果。

下面的代码演示了TryUpdate()方法的应用。

C#
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        ConcurrentDictionary<string, string> dict =
            new ConcurrentDictionary<string, string>();
        dict["key1"] = "value1";
        dict["key2"] = "value2";
        //
        tWeb.WriteLine(dict.TryUpdate("key1","valueA","value1"));
        tWeb.WriteLine(dict.TryUpdate("key2","valueB","value"));
        tWeb.WriteLine(dict.TryUpdate("key3", "valueC", "value"));
    }
}

执行代码会显示True、False、False。第一个输出,元素“key1/value1”存在,会将其值修改为valueA,方法返回true。第二个输出,键key2存在,但对应的值不是value,更新失败,方法返回false。第三个输出,键key3不存在,方法同样返回false。

tPair和tPairList类

在.NET Framework类库中,System.Collections、System.Collections.Generic和System.Collections.Concurrent等命名空间中包含了大量的集合处理类型,如数组、列表、栈(Stack)、队列(Queue)等;应用开发中,我们还可以根据需要创建自己的集合类型,这也是本节将要完成的工作。

tPair类

首先是tPair类,其功能比较简单,它表示一个“键/值”对象,并定义了常用类型的转换方法,如下面的代码(/app_code/data/base/tPair.cs)。

C#
using System;

public class tPair
{
    public string Name { get; set; }
    public object Value { get; set; }
    //
    public static tPair Get(string sName="",object oValue = null)
    {
        return new tPair() { Name = sName, Value = oValue };
    }
    //
    public int GetInt() { return tInt.Parse(Value); }
    public long GetLng() { return tLng.Parse(Value); }
    public float GetSng() { return tSng.Parse(Value); }
    public double GetDbl() { return tDbl.Parse(Value); }
    public decimal GetDec() { return tDec.Parse(Value); }
    public string GetStr() { return tStr.Parse(Value); }
    public bool GetBool() { return tBool.Parse(Value); }
    public DateTime GetDate() { return tDate.Parse(Value); }
}

tPair类中定义了Name和Value属性,分别表示数据名称和数据的值中;构造函数中,数据名称的默认值为空字符串,数据值的默认值为null;此外,在一系列类型转换方法中,调用了封装的对应类型的Parse()方法,将Value属性直接转换为对应的类型,转换失败时会返回相应类型的默认值。

tPairList类

tPairList类用于处理tPair对象组成的集合,实现为List<tPair>的子类,基本定义如下(/app_code/data/base/tPairList.cs)。

C#
using System;
using System.Collections.Generic;

public class tPairList : List<tPair>
{
    public tPairList()
    {
    }
    //
    public static tPairList Get(params tPair[] e)
    {
        tPairList pl = new tPairList();
        pl.AddRange(e);
        return pl;
    }
    // 其它代码…
}

代码中除了无参数的构造函数,还定义了Get()静态方法,可以通过若干个tPair对象创建tPairList对象。

tPairList类继承于List<tPair>类,除了继承List泛型类的操作成员,还可以根据需要进行扩展,如下面的Add()方法。

C#
//
public void Add(string sName,object oValue)
{ 
    Add(tPair.Get(sName,oValue)); 
}
//
public void Add(params tPair[] p)
{
    AddRange(p);
}

第一个Add()方法,使用数据名称和数据值添加一个列表元素;第二个Add()方法,可以将若干个tPair对象添加为列表对象。

此外,在tPairList类中还定义了如下操作方法。

C#
public int Find(string sName)
{
    for(int i = 0; i < Count; i++)
    {
        if (this[i].Name == sName) 
            return i;
    }
    return -1;
}
//
public bool Exists(string sName)
{
    for (int i = 0; i < Count; i++)
    {
        if (this[i].Name == sName)
            return true;
    }
    return false;
}
//
public tPair GetPair(string sName)
{
    for (int i = 0; i < Count; i++)
    {
        if (this[i].Name == sName)
            return this[i];
    }
    return tPair.Get();
}

代码中,Find()方法通过数据名查询列表元素,找到后返回索引值,没有找到时返回-1。Exists()方法判断指定数据名的元素是否存在,存在时返回true,否则返回false。GetPair()方法则通过数据名返回列表元素,没有找到时返回空的tPair对象,而不是null。

tPairList和tPair类的应用特点是,tPair对象表示的数据包含了数据名称和数据的值,并可以方便地转换为各种常用的数据类型;而对于tPairList列表,可以按照数值索引读取元素,方便操作tPair对象的有序集合。