c# 扩展方法奇思妙用基础篇五:Dictionary<TKey, TValue> 扩展

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);         //不存在则返回 null
var 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);

线程安全:

为了演示简单,本文中的代码没有考虑线程安全的问题,不宜在实际项目中直接使用!

线程安全请使用 ConcurrentDictionary<TKey, TValue> 类(.Net 4新增),参考以下文章:

http://www.cnblogs.com/ldp615/archive/2011/01/28/dictionary-extensions.html

时间: 2024-04-19 16:48:02

c# 扩展方法奇思妙用基础篇五:Dictionary<TKey, TValue> 扩展的相关文章

Python基础篇(五)

bool用于判断布尔值的结果是True还是False >>> bool("a") True >>> bool(3) True >>> bool("") False >>> bool(0) False Python中的elif类似于Java中的elseif >>> number = (int)(input("input a number: ")) input

linux基础篇-12,grep正则表达式与扩展正则表达式

################################################ 基本正则表达式:grep 使用正则表达式定义的模式来过滤文本 grep -i :不区分大小写 --color -v:显示没被匹配的任意行 -o:只显示被匹配的字符串 -E 扩展的正是表达式 =egerp -A#:#为数字 -B#: -C#: [[email protected] testcp]# grep -o 'root' /etc/passwd --color root root root ro

Hybrid APP基础篇(五)-&gt;JSBridge实现示例

说明 JSBridge实现示例 目录 前言 参考来源 楔子 JS实现部分 说明 实现 Android实现部分 说明 JSBridge类 实现 Callback类 实现 Webview容器关键代码 实现 API 类实现 iOS实现部分 说明 WebViewJavascriptBridgeBase 实现 WebViewJavascriptBridge 实现 Webview容器关键代码 实现 前言 参考来源 前人栽树,后台乘凉,本文参考了以下来源 Hybrid APP架构设计思路 marcuswest

传智的光辉岁月-C#基础篇五值类型和引用类型

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace P01Method { class Program { static void Main(string[] args) { //int a1 = 11; //int b2 = 22; //Add2Num(a1, b2);//在调用方法时,为 方法括号中 传递的 值 就叫做 实参(实际参数) //Add2Nu

Linux基础篇五——缓冲

在现代操作系统里几乎所有的I/O设备在和处理机交换数据时都使用了缓冲机制,缓冲区是一个存储区域,可以是专门的硬件寄存器组成但是因为硬件的成本较高容量也小,一般的情况下,更多的利用内存来作为缓冲区. 缓冲区管理:组织缓冲区并提供获得和释放缓冲区的手段. 缓冲技术是为了协调吞吐速度相差很大的设备之间数据调用而采用的技术 缓冲的引用要解决的问题实际上有: 1.改善CPU和I/O设备之间速度不匹配的情况. 2.可以减少I/O设备对CPU的中断次数及放宽对CPU的中断响应时间要求. 3.提高CPU和I/O

Linux基础篇五——find it !

我们知道在Linux中有许多许多的文件,有的时候凭借我们的记忆我们可能要找很久很久才能够找到我们所需要的文件,之前我们稍有提到过一个命令叫做find 它确实是一个十分重要的命令. find的基本用法: 和时间有关的参数:-atime.-ctime.-mtime.-newer ** 以-mtime为例,看看与事件有关的选项的用法: -mtime n:在n天之前的"一天之内"被更改的文件 -mtime +n:在n天之前(不包含n这天)被更改过的文件 -mtime -n:在n天之内(包含n这

Java面试题-基础篇五

41.a.hashCode() 有什么用?与 a.equals(b) 有什么关系?hashCode() 方法对应对象整型的 hash 值.它常用于基于 hash 的集合类,如 Hashtable.HashMap.LinkedHashMap等等.它与 equals() 方法关系特别紧密.根据 Java 规范,两个使用 equal() 方法来判断相等的对象,必须具有相同的 hash code.42.字节流与字符流的区别要把一段二进制数据数据逐一输出到某个设备中,或者从某个设备中逐一读取一段二进制数据

Lua 学习之基础篇五&lt;Lua 之 OS 库&gt;

lua os库提供了简单的跟操作系统有关的功能 1.os.clock() 返回程序所运行使用的时间 local nowTime = os.clock() print("now time is ",nowTime) local s = 0 for i = 1,100000000 do s =s+i end spendTime = os.clock() - nowTime print(string.format("Spend time is : %.2f\n", spe

基础篇五:Nginx的目录和基础配置

Yum安装目录:yum的方式安装 rpm -ql nginx 下面开始安装目录详解 原文地址:https://www.cnblogs.com/yujianadu/p/12103733.html