|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI hope I am not the only person to have ever experienced the dreaded Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. This leaves everyone wondering, what was the key? What was the dictionary? Certainly, the programmer could wrap every line of code that uses a dictionary indexer with a The DiagnosticDictionary ClassTo help with this problem, I've created a ImplementationCopy and paste the following code, and replace your 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;}
}
}
}
Given the test code: DiagnosticDictionary<string, string> d2 =
new DiagnosticDictionary<string, string>("Test");
string b = d2["b"];
the Unhandled Exception: System.Collections.Generic.KeyNotFoundException: The key
'b' was not found in the dictionary 'Test'
Note that it now tells you the key and the dictionary name. Alternate Implementation: Extension MethodsThe following illustrates using an Extension Method (courtesy of CPian Wouter Ballet, see the article comments below): 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.");
}
}
}
You will, though, have to change how you index the key: Dictionary<string, string> d2 = new Dictionary<string, string>();
string b = d2.DiagItem("b");
So, I think it's more an architectural choice that one needs to make early in the project. In my opinion, if you are starting a project, then using an Extension Method makes more sense than refactoring all the indexers in an existing project. Why Wouldn't You Use DiagnosticDictionary?First is the performance hit of going through the Second is security. Let's say, you have a dictionary defined like (dubious at best, but it's an example): Dictionary<Hash password, Rights rights>
You certainly wouldn't want the exception to emit the password key! What About That ToString() ?The call to ConclusionHopefully, people will find this implementation as useful as my client's QA folks, the DB admin guru, devs, and even myself. :) Special thanks to CPian Wouter Ballet for showing me how to use Generics in an Extension Method.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||