2

Possible Duplicate:
How to get null instead of the KeyNotFoundException accessing Dictionary value by key?

I currently have lots of Dictionary<string, T> uses in my project, and most of them look like so:

if (myDic.ContainsKey("some key"))
    localVar = myDic["some key"];

It's not very effecient too, as it does two calls to the dictionary, which can be resource consuming. TryGetValue() is a cool thing, but it just doesn't do it in one line.

I just want to get null if there is no such key from var v = myDic[key]. How do I do that?

Community
  • 1
  • 1
user1306322
  • 8,561
  • 18
  • 61
  • 122
  • If you are not inclined to change your Dictionaries as mentioned in Konstantin's answers, a simple extension method which implements the null return would also work. – Ravi Y Jan 16 '13 at 05:48
  • 3
    Side note: value types does not have have good notion of `null`... so beware of `Dictionary` and trying to return "special non existent" value of type `int` :) – Alexei Levenkov Jan 16 '13 at 05:57
  • @AlexeiLevenkov good point. When I solved this issue, I didn't have to work with dictionaries having values of value type – horgh Jan 16 '13 at 05:59
  • There's always an `int?` for those cases. – user1306322 Jan 16 '13 at 06:07
  • With Linq. you can do something like what was propossed in this related question: http://stackoverflow.com/questions/5424968/dictionary-firstordefault-how-to-determine-if-a-result-was-found – dutzu Jan 16 '13 at 05:48

3 Answers3

4

You may use an extension method with TryGetValue:

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

thanks to which you'll be able to write

somedict.GetValueByKeyOrNull("key1")

In the end trying to do this very thing I came up with a variant using a deriving from dictionary class with explicit interface implementation: How to get null instead of the KeyNotFoundException accessing Dictionary value by key?

That is

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;
            dict.TryGet(key, out val);
            return val;
        }
    }
}

and use it instead of the original dictionary everywhere:

//create some dictionary
NullValueDictionary<int, string> dict = new NullValueDictionary<int, string>
{
    {1,"one"}
};
//have a reference to the interface
INullValueDictionary<int, string> idict = dict;
string val = idict[2]; // null
val = idict[1];        // "one"
Community
  • 1
  • 1
horgh
  • 17,918
  • 22
  • 68
  • 123
  • Is there any way I could just use the square brackets, as with a normal dictionary? – user1306322 Jan 16 '13 at 05:52
  • @user1306322 did you read the thread I mentioned under this question? There you may find ideas – horgh Jan 16 '13 at 05:53
  • @user1306322 I've shared the ideas from that thread in this question. Please consider. – horgh Jan 16 '13 at 05:58
  • I understand it only serves a purpose of example, but I wouldn't recommend using a `Dictionary` where `T[]` can be much faster. – user1306322 Jan 16 '13 at 06:14
  • @user1306322 this is indeed only an example. In reality both `T` and `U` are pf type `string`...I'd say, this is a little out of the question's scope – horgh Jan 16 '13 at 06:18
  • This is a good answer, so I prefer to return some kind of `Optional` type, that can contain value or not :) – Evgeny Lazin Jan 16 '13 at 06:28
1

I don't like to deal with null so my implementation will look like this:

interface Maybe<T> {
    bool HasValue {get;}
    T Value {get;}
}

class Nothing<T> : Maybe<T> {
    public bool HasValue { get { return false; } }
    public T Value { get { throw new Exception(); } }
    public static const Nothing<T> Instance = new Nothing<T>();
}

class Just<T> : Maybe<T> {
    private T _value;
    public bool HasValue { get { return true; } }
    public T Value { get { return _value; } }
    public Just(T val) {
        _value = val;
    }
}

Maybe is a object that can contain value or not. Note that Nothing class contains static field Instance. We can use this value instead of creating new value each time we need to return Nothing from function.

Now, we need to create our own dictionary class:

class MyDictionary<TKey, TValue>
{
    private Dictionary<TKey, TValue> _dict;

    ...

    public Maybe<TValue> this[TKey key] {
        TValue val;
        if (_dict.TryGetValue(key, out val)) {
            return new Just<TValue>(val);
        return Nothing<TValue>.Instance;
    }
}

Advantage of this approach is not clear, because C# doesn't have pattern matching. But it can be emulated with dynamic:

void ProcessResult(Just<string> val) {
    Console.WriteLine(val);
}

void ProcessResult(Nothing<string> n) {
    Console.WriteLine("Key not found");
}

var dict = new MyDictionary<string, string>();
...
dynamic x = dict["key"];
ProcessResult(x);

I think that this is very clear way to express the fact that dictionary can't always return meaningful result. Also it is obvious for reader that function overload ProcessResult(Just<T>) will be called only for values that present in dictionary and other overload will be called in case when key is not found.

Pros:

  • Type serves as a specification.
  • Dictionary can contain both value and reference types.

Cons:

  • More keystrokes.
  • Little more complexity to deal with.
Evgeny Lazin
  • 9,193
  • 6
  • 47
  • 83
0

I decided to do it like this:

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

It lets me use it like any other dictionary, through square brackets. Since I'm not going to use this with value types as TValue, I think it's good enough a solution.

user1306322
  • 8,561
  • 18
  • 61
  • 122
  • What if I want to create MyDictionary? How can I check whether key present in the dictionary or not? – Evgeny Lazin Jan 16 '13 at 07:03
  • @Lazin As I specifically mentioned above, I'm not going to use this with value types as `TValue`, and so shouldn't you. Alternatively, you can try using `MyDictionary`, or better yet `int?[]`. – user1306322 Jan 16 '13 at 07:14