111

In some certain scenario it appeared to be useful for me to have a short-spoken, readable way to get null instead of the KeyNotFoundException while accessing dictionary value by key, when there is no such key in the dictionary.

The first thing that came into my mind was an extension method:

public static U GetValueByKeyOrNull<T, U>(this Dictionary<T, U> dict, T key)
        where U : class //it's acceptable for me to have this constraint
{
    if (dict.ContainsKey(key))
        return dict[key];
    else 
        //it could be default(U) to use without U class constraint
        //however, I didn't need this.
        return null; 
}

But it's not very short-spoken actually, when you write something like this:

string.Format("{0}:{1};{2}:{3}",                                                 
              dict.GetValueByKeyOrNull("key1"),
              dict.GetValueByKeyOrNull("key2"),
              dict.GetValueByKeyOrNull("key3"),
              dict.GetValueByKeyOrNull("key4"));

I'd say, it would be much better to havesomething close to base syntax: dict["key4"].

Then I came up with an idea to have a class with a private dictionary field, which exposed the functionality I need:

public class MyDictionary<T, U> //here I may add any of interfaces, implemented
                                //by dictionary itself to get an opportunity to,
                                //say, use foreach, etc. and implement them
                                // using the dictionary field.
        where U : class
{
    private Dictionary<T, U> dict;

    public MyDictionary()
    {
        dict = new Dictionary<T, U>();
    }

    public U this[T key]
    {
        get
        {
            if (dict.ContainsKey(key))
                return dict[key];
            else
                return null;
        }
        set
        {
            dict[key] = value;
        }
    }
}

But it seems a little overhead to get the slight change in the basic behaviour.

One more workaround could be to define a Func in the current context like this:

Func<string, string> GetDictValueByKeyOrNull = (key) =>
{
    if (dict.ContainsKey(key))
        return dict[key];
    else
        return null;
};

so it could be utilized like GetDictValueByKeyOrNull("key1").

Could you, please, give me any more suggestions or help to choose a better one?

horgh
  • 17,918
  • 22
  • 68
  • 123
  • This isn't much shorter, but assuming the value's nullable, a `Lookup` won't throw an exception - `lookup[key].FirstOrDefault()`. – Simon MᶜKenzie Jan 04 '13 at 03:17
  • @SimonMcKenzie thanks, I'll think of it – horgh Jan 04 '13 at 03:19
  • 1
    related: http://stackoverflow.com/questions/538729/is-there-an-idictionary-implementation-that-returns-null-on-missing-key-instead?rq=1 – nawfal Apr 06 '13 at 08:11
  • 2
    Maybe this is new, but why don't we just use `dict.TryGetValue`? http://msdn.microsoft.com/en-us/library/bb347013(v=vs.110).aspx – Jess Jul 01 '14 at 19:45
  • 1
    @Jess it's not as short-spoken as I wanted – horgh Jul 01 '14 at 23:34
  • @Jess out parameters are unpleasant and should be avoided unless absolutely necessary. Just check to see if the key is there - `valuetype? v = dictionary.ContainsKey(k) : (valuetype?) dictionary[k] : null` – James Moore Mar 21 '17 at 20:44
  • Agreed. I don't use TryGetValue anymore. I just check `ContainsKey` like you said. – Jess Mar 22 '17 at 12:47
  • This question isn't a duplicate of https://stackoverflow.com/questions/538729 as suggested, since the default(ValueType) is not null. – shannon Dec 16 '18 at 22:15
  • @JamesMoore : re-entrant code may care that a ContainsKey check performs an additional de-ref. – shannon Dec 16 '18 at 22:16

7 Answers7

96

Here is my solution from my personal library, implemented as an extension method. I am only posting it because it is implemented from the dictionary interface and allows an optional default value to be passed in.

Implementation

public static TV GetValue<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue = default(TV))
{
    TV value;
    return dict.TryGetValue(key, out value) ? value : defaultValue;
}

Usage

 MyDictionary.GetValue("key1");
 MyDictionary.GetValue("key2", -1);
 MyDictionary.GetValue("key3")?.SomeMethod();
Community
  • 1
  • 1
Jared G
  • 1,578
  • 1
  • 13
  • 12
  • 13
    Best solution if you ask me. This can now even be a single line call in C# 7 with inline out variables. – Mladen Mihajlovic Oct 25 '17 at 06:54
  • 8
    Mladen means `TryGetValue(key, out TV value)` – silvalli Sep 02 '18 at 04:18
  • 3
    Since Core 2.0, there is a GetValueOrDefault extension method that does the same. (actually 2 methods, one with the defaultValue parameter and one without) https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.getvalueordefault – Daniel Feb 25 '22 at 13:42
39

You can't get the syntax you want with an extension method, and as others have said overriding a method/operator to change its behavior is generally not a great idea. I think the best you can do is shorten the name you use.

That's if you need to keep to the IDictionary interface. If you aren't interfacing with any code that expects an IDictionary, then you are free to define your own interface and having the [] operator work differently isn't a problem.

Whatever you end up calling the function, you'll want to implement it like this:

public static U Get<T, U>(this Dictionary<T, U> dict, T key)
    where U : class
{
    U val;
    dict.TryGetValue(key, out val);
    return val;
}

It just does one lookup, compared with 2 for your implementations.

Evan Cox
  • 187
  • 1
  • 14
bmm6o
  • 6,187
  • 3
  • 28
  • 55
27

In the end I came up with a variant using a deriving from dictionary class with explicit interface implementation:

public interface INullValueDictionary<T, U>
    where U : class
{
    U this[T key] { get; }
}

public class NullValueDictionary<T, U> : Dictionary<T, U>, INullValueDictionary<T, U>
    where U : class
{
    U INullValueDictionary<T, U>.this[T key]
    {
        get
        {
            U val;
            this.TryGetValue(key, out val);
            return val;
        }
    }
}

So it exposes the functionality I need the following way:

//create some dictionary
NullValueDictionary<int, string> dict = new NullValueDictionary<int, string>
{
    {1,"one"}
};
//have a reference to the interface
INullValueDictionary<int, string> idict = dict;

try
{
    //this throws an exception, as the base class implementation is utilized
    Console.WriteLine(dict[2] ?? "null");
}
catch { }
//this prints null, as the explicit interface implementation 
//in the derived class is used
Console.WriteLine(idict[2] ?? "null");
OfirD
  • 9,442
  • 5
  • 47
  • 90
horgh
  • 17,918
  • 22
  • 68
  • 123
  • It's also possible to ditch the interface and replace `U INullValueDictionary.this[T key]` with `new public U this[T key]`. – OfirD Jan 07 '21 at 15:19
23

Add a DictionaryExtension class

public static class DictionaryExtension
{
    public static TValue GetValueOrDefault<TKey, TValue>
        (   this IDictionary<TKey, TValue> dictionary,TKey key)
    {
        TValue value;
        return dictionary.TryGetValue(key, out value) ? value : default(TValue);
    }
}

And it can return Default value if not found key in dictionary.

The Default is null if this is reference type .

_dic.GetValueOrDefault();
Neuron
  • 5,141
  • 5
  • 38
  • 59
TimChang
  • 2,249
  • 13
  • 25
8

Worth pointing out that the HybridDictionary does this by default. You lose the generic typeing, but get the null-if-not-found functionality.

And (at least theoretically) you get performance benefits at low numbers of values, I think.

Neil
  • 7,482
  • 6
  • 50
  • 56
Brondahl
  • 7,402
  • 5
  • 45
  • 74
5

I premise by saying that I would not use this. The new keyword, while useful in this case, can create bugs which are really hard to find. Other than that, you can try this class.

class MyDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public new TValue this[TKey key]
    {
        get
        {
            TValue value;
            return TryGetValue(key, out value) ? value : default(TValue);
        }
        set { base[key] = value; }
    }
}
e_ne
  • 8,340
  • 32
  • 43
  • 7
    Yes, this violates **[Liskov Substitution Principle](http://en.wikipedia.org/wiki/Liskov_substitution_principle)** and bugs created by this are really nasty. – Honza Brestan Jan 04 '13 at 03:30
  • I wouln't use this either))) Anyway, thanks – horgh Jan 04 '13 at 03:48
  • 1
    @HonzaBrestan Can you explain? Thanks – arao6 Sep 21 '14 at 21:56
  • 8
    @arao6 This would change the contract of the `Dictionary` class. Instead of the exception, you will get a default value. This means that null reference exceptions can now occur when a key is missing, and that you cannot tell when the key is missing and when it is present, but contains a null value. Consider a 3rd party method that accepts a `Dictionary`, depends on its behavior, but you supply `MyDictionary` instead, with this new, conflicting behaviour. The LSP says subclasses should be safe (correct) to use wherever the base class is used. This clearly breaks it due to changes in contract. – Honza Brestan Sep 22 '14 at 07:40
  • 5
    @arao6 Another way to look at this is through the type system - think the exception is a part of the method signature (extension of the return type) and so is the behavior "throws the exception when key is missing" (C# type system cannot express this of course, but we can think it can for now). Then this new `MyDictionary` indexer would have a different return type than the original one in the base class - probably not what you want when creating a subclass. – Honza Brestan Sep 22 '14 at 07:50
0

I've done this before and it worked pretty well to just inherit the regular Dictionary class and just hide the indexer. It's really clean to do it this way so you automatically acquire all the reliability and familiarity of the Regular Dictionary class.

public class NullSafeDict<TKey, TValue> : Dictionary<TKey, TValue> where TValue : class
{
    public new TValue this[TKey key]
    {
        get
        {
            if (!ContainsKey(key))
                return null;
            else
                return base[key];
        }
        set
        {
            if (!ContainsKey(key))
                Add(key, value);
            else
                base[key] = value;
        }
    }
}
Michael Socha
  • 1,748
  • 1
  • 16
  • 17