Andreas
Andreas

Reputation: 7248

The given key was not present in the dictionary. Which key?

Is there a way to get the value of the given key in the following exception in C# in a way that affects all generic classes? I think this is a big miss in the exception description from Microsoft.

The given key was not present in the dictionary.

A better way would be:

The given key '" + key.ToString() + "' was not present in the dictionary.

Solutions might involve mixins or derived classes maybe.

Upvotes: 127

Views: 441762

Answers (8)

Greg
Greg

Reputation: 1629

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);}
}

Upvotes: 1

Stelios Adamantidis
Stelios Adamantidis

Reputation: 2031

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:

System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>
System.Collections.IDictionary
System.Runtime.Serialization.IDeserializationCallback
System.Runtime.Serialization.ISerializable

Quite an overkill "just" for an exception message.

Upvotes: 15

Rohim Chou
Rohim Chou

Reputation: 1269

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.");
    }
}

Upvotes: 2

BradleyDotNET
BradleyDotNET

Reputation: 61349

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!

Upvotes: 98

Nitika Chopra
Nitika Chopra

Reputation: 1405

You can try this code

Dictionary<string,string> AllFields = new Dictionary<string,string>();  
string value = (AllFields.TryGetValue(key, out index) ? AllFields[key] : null);

If the key is not present, it simply returns a null value.

Upvotes: 1

Sharon AS
Sharon AS

Reputation: 364

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.

Upvotes: 2

gabba
gabba

Reputation: 2880

If you want to manage key misses you should use TryGetValue

https://msdn.microsoft.com/en-gb/library/bb347013(v=vs.110).aspx

string value = "";
if (openWith.TryGetValue("tif", out value))
{
    Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
    Console.WriteLine("Key = \"tif\" is not found.");
}

Upvotes: 11

Sam Harwell
Sam Harwell

Reputation: 99869

In the general case, the answer is No.

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.

Upvotes: 20

Related Questions