DiagnosticDictionary






4.11/5 (10投票s)
让“给定的键在字典中不存在”消息更具信息性。
引言
我希望我不是唯一一个经历过可怕的KeyNotFoundException
消息的人
未处理的异常:System.Collections.Generic.KeyNotFoundException:给定的键在字典中不存在。
这让每个人都想知道,这个键是什么?这个字典是什么?
当然,程序员可以用try-catch
块包装每一行使用字典索引器的代码,或者至少是某个外部方法,但即使这样,异常也不会告诉你导致异常的键。所以,这是一篇简短的文章,介绍了一个简单的实现,它覆盖了Dictionary<>
类。我还探讨了扩展方法是否适合实现所需的行为。
DiagnosticDictionary 类
为了帮助解决这个问题,我创建了一个DiagnosticDictionary
,它覆盖了(带有令人讨厌的“new
”关键字)泛型字典的索引器。它捕获KeyNotFoundException
,并尝试描述键和字典名称(可以在构造函数中提供)重新抛出它。作为奖励,我还添加了一个Tag
属性,可用于将任何对象与字典关联。
实现
复制并粘贴以下代码,并将您的Dictionary
构造函数替换为DiagnosticDictionary
。
using System;
using System.Collections.Generic;
namespace Clifton.Collections.Generic
{
/// <summary>
/// A dictionary with an indexer that produces an informative
/// KeyNotFoundException message.
/// </summary>
public class DiagnosticDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
protected object tag;
protected string name = "unknown";
/// <summary>
/// Gets/sets an object that you can associate with the dictionary.
/// </summary>
public object Tag
{
get { return tag; }
set { tag = value; }
}
/// <summary>
/// The dictionary name. The default is "unknown".
/// Used to enhance the KeyNotFoundException.
/// </summary>
public string Name
{
get { return name; }
set { name = value; }
}
/// <summary>
/// Parameterless constructor.
/// </summary>
public DiagnosticDictionary()
{
}
/// <summary>
/// Constructor that takes a name.
/// </summary>
public DiagnosticDictionary(string name)
{
this.name = name;
}
/// <summary>
/// Indexer that produces a more useful KeyNotFoundException.
/// </summary>
public new TValue this[TKey key]
{
get
{
try
{
return base[key];
}
catch (KeyNotFoundException)
{
throw new KeyNotFoundException("The key '" + key.ToString() +
"' was not found in the dictionary '"+name+"'");
}
}
set { base[key] = value;}
}
}
}
给定测试代码
DiagnosticDictionary<string, string> d2 =
new DiagnosticDictionary<string, string>("Test");
string b = d2["b"];
DiagnosticDictionary
将抛出一个更有用的异常
Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The key
'b' was not found in the dictionary 'Test'
请注意,它现在会告诉你键和字典名称。
替代实现:扩展方法
以下说明了使用扩展方法(由 CPian Wouter Ballet 提供,请参阅下面的文章评论)
public static class DiagnosticDictionary
{
public static TValue DiagItem<TKey, TValue>(this IDictionary<TKey,
TValue> d, TKey key)
{
try
{
return d[key];
}
catch (KeyNotFoundException)
{
throw new KeyNotFoundException("The key '" + key +
"' was not found in the dictionary.");
}
}
}
但是,您必须更改索引键的方式
Dictionary<string, string> d2 = new Dictionary<string, string>();
string b = d2.DiagItem("b");
所以,我认为这更多的是一个架构选择,需要在项目早期做出。我认为,如果您要启动一个项目,那么使用扩展方法比重构现有项目中的所有索引器更有意义。
为什么不使用 DiagnosticDictionary?
首先是通过DiagnosticDictionary
索引器的性能损失,然后它会调用基类索引器。
其次是安全性。假设,你定义了一个像这样的字典(充其量是可疑的,但这是一个例子)
Dictionary<Hash password, Rights rights>
你当然不希望异常发出密码键!
关于 ToString() 呢?
调用key.ToString()
对于值类型和字符串来说很好。如果你的键有更复杂的结构或类,那么你可以考虑重写ToString()
方法来提供对 struct/class 内容的更具信息性的描述。
结论
希望人们会发现这个实现和我的客户的 QA 人员、数据库管理员、开发人员,甚至我自己一样有用。 :)
特别感谢 CPian Wouter Ballet 向我展示如何在扩展方法中使用泛型。