However, you can set the debugger to break at the point where the exception is first thrown. At that time, the key which was not present will be accessible as a value in the call stack.
In Visual Studio, this option is located here:
Debug → Exceptions... → Common Language Runtime Exceptions → System.Collections.Generic
There, you can check the Thrown box.
For more specific instances where information is needed at runtime, provided your code uses IDictionary<TKey, TValue> and not tied directly to Dictionary<TKey, TValue>, you can implement your own dictionary class which provides this behavior.
string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
Console.WriteLine("Key = \"tif\" is not found.");
}
This exception is thrown when you try to index to something that isn't there, for example:
Dictionary<String, String> test = new Dictionary<String,String>();
test.Add("Key1","Value1");
string error = test["Key2"];
Often times, something like an object will be the key, which undoubtedly makes it harder to get. However, you can always write the following (or even wrap it up in an extension method):
if (test.ContainsKey(myKey))
return test[myKey];
else
throw new Exception(String.Format("Key {0} was not found", myKey));
Or more efficient (thanks to @ScottChamberlain)
T retValue;
if (test.TryGetValue(myKey, out retValue))
return retValue;
else
throw new Exception(String.Format("Key {0} was not found", myKey));
Microsoft chose not to do this, probably because it would be useless when used on most objects. Its simple enough to do yourself, so just roll your own!
string Value = dic.ContainsKey("Name") ? dic["Name"] : "Required Name"
With this code, we will get string data in 'Value'. If key 'Name' exists in the dictionary 'dic' then fetch this value, else returns "Required Name" string.
Borrowed from @BradleyDotNET's anwser, here's the extension method version:
public static TValue GetValue<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key)
{
if (dictionary.TryGetValue(key, out TValue result))
{
return result;
}
else
{
throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
}
}
If anyone still cares in 2021 and can afford to move to .NET Core, .Net 5 finally tells us the name of the offending key:
class Program
{
static void Main(string[] args)
{
Dictionary<string, object> dictionary = new()
{
["foo"] = new object()
};
Console.WriteLine(dictionary["bar"]);
}
}
Will give you:
Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'bar' was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at KnfTest.Program.Main(String[] args)
The same code in .Net Framework 4.8 gives the old dreaded message.
As for those who can't afford moving to Core yet I'm afraid there is no real working solution. Replacing all indexer invocations with TryGetValue is cumbersome -not to say absurd. Subclassing from Dictionary is practically impossible. Yes you can declare a new indexer but then polymorphism goes out the window as your indexer will be called only when using a reference of the subclass. Finally, creating your own Dictionary wrapper might be a solution but that's again a non trivial process, especially if you want to replicate exactly the Dictionary which implements not only IDictionary<TKey,TValue> but also:
In C# 7.0 or greater, you can Use the out parameter modifer to solve your problem in one line, and only make one call to the indexer. It works in c# 7.0 or greater.
public TItem GetOrException<TKey,TItem>(TKey key) where TKey : IFormattable =>
dictionary.TryGetValue(key, out var item) ? item : throw new KeyNotFoundException( "Key: " + key.ToString() );
You can also opt for the longer try catch block which gives better exception information:
public TItem GetOrException<TKey,TItem>(TKey key) where TKey : IFormattable {
try{ return dictionary[key]; }
catch (KeyNotFoundException e) { throw new KeyNotFoundException("Key:" + key.ToString(), e);}
}