Dictionary<TKey, TValue> 類是常用的一個基礎類,但用起來有時確不是很方便。本文逐一討論,並使用擴充方法解決。
向字典中添加鍵和值
添加鍵和值使用 Add 方法,但很多時候,我們是不敢輕易添加的,因為 Dictionary<TKey, TValue> 不允許重複,嘗試添加重複的鍵時 Add 方法引發 ArgumentException。
大多時候,我們都會寫成以下的樣子:
var dict = new Dictionary<int, string>();// ...// 情形一:不存在才添加if (dict.ContainsKey(2) == false) dict.Add(2, "Banana");// 情形二:不存在添加,存在則替換if (dict.ContainsKey(3) == false) dict.Add(3, "Orange");else dict[3] = "Orange";
其實,第二種情形可以寫如下書寫(請參見 http://msdn.microsoft.com/zh-cn/library/9tee9ht2.aspx):
dict[3] = "Orange";
不過好多朋友都會對這種方式表示疑慮,不太確定這樣會不會出問題。
不管是上面的哪種寫法,用字典時最大的感覺就是擔心,怕出異常,因此代碼會寫的很羅嗦。
我每次用字典時都這樣,時間長了,實在是厭煩了,索性擴充一下,用以下兩個方法來應對上面兩種情形:
/// <summary>/// 嘗試將鍵和值添加到字典中:如果不存在,才添加;存在,不添加也不拋導常/// </summary>public static Dictionary<TKey, TValue> TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value){ if (dict.ContainsKey(key) == false) dict.Add(key, value); return dict;}/// <summary>/// 將鍵和值添加或替換到字典中:如果不存在,則添加;存在,則替換/// </summary>public static Dictionary<TKey, TValue> AddOrReplace<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value){ dict[key] = value; return dict;}
TryAdd 和 AddOrReplace 這兩個方法具有較強自我描述能力,用起來很省心,而且也簡單:
dict.TryAdd(2, "Banana");dict.AddOrReplace(3, "Orange");
或者像 Linq 或 jQuery 一樣連起來寫:
dict.TryAdd(1, "A") .TryAdd(2, "B") .AddOrReplace(3, "C") .AddOrReplace(4, "D") .TryAdd(5, "E");
再來看另外一個問題:
擷取值
從字典中擷取值通常使用如下方式:
string v = "defaultValue";// 方式一if (dict.ContainsKey(3)) v = dict[3];// 方式二bool isSuccess = dict.TryGetValue(3, out v);
使用索引的方式擷取前一定先判斷,否則不存在時會引發 KeyNotFoundException 異常。
我尤其討厭第二種方式,因為採用 out 要提前聲明一個變數,代碼至少要兩行,不夠簡潔。
看下 GetValue 擴充:
/// <summary>/// 擷取與指定的鍵相關聯的值,如果沒有則返回輸入的預設值/// </summary>public static TValue GetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default(TValue)){ return dict.ContainsKey(key) ? dict[key] : defaultValue;}
使用方便:
var v1 = dict.GetValue(2); //不存在則返回 nullvar v2 = dict.GetValue(2, "abc"); //不存在返回 ”abc“
一行代碼能搞定。
大量新增
List<T> 類有個 AddRange 方法,可以不用 foreach 迴圈直接向當前集合加入另外一個集合:
List<string> roles = new List<string>();roles.AddRange(new[] { "role2", "role2" });roles.AddRange(user.GetRoles());
相當方便,可憐 Dictionary<TKey, TValue> 類沒有,幸好有擴充方法:
/// <summary>/// 向字典中大量新增索引值對/// </summary>/// <param name="replaceExisted">如果已存在,是否替換</param>public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<KeyValuePair<TKey, TValue>> values, bool replaceExisted){ foreach (var item in values) { if (dict.ContainsKey(item.Key) == false || replaceExisted) dict[item.Key] = item.Value; } return dict;}
使用樣本:
var dict1 = new Dictionary<int, int>() .AddOrReplace(2, 2) .AddOrReplace(3, 3);var dict2 = new Dictionary<int, int>() .AddOrReplace(1, 1) .AddOrReplace(2, 1) .AddRange(dict1, false);