views:

34

answers:

2

We wanted to assign/add an item to a Dictionary using the key index, like this:

Dictionary<string, object> dict = new Dictionary<string, object>();
dict["key"] = dict["key"] ?? "object";

But it results to: "The given key was not present in the dictionary."

Is there any way we could assign values to this dictionary the same as the way I assign values to sessions, using null coalesce:

Session["Member"] = Session["Member"] ?? new Member();

Thanks

Edit: I actually tried to override the indexer from a child class of dictionary, but to no avail. Implementing IDictionary interface would seem too much because we just need the functionality of the code below (i.e., return null if key is not found). So I think, maybe we could use extensions like:

public static TValue [this Dictionary<TKey, TValue> thisDictionary, TKey key] {
    get { return thisDictionary.ContainsKey(key) ? thisDictionary[key] : null; }
    set { thisDictionary[key] = value; }
}

but it is not working or even compiling.

Any other simpler ways so we could reuse this indexer functionality? Thanks a lot.

+1  A: 

As you noted, trying to fetch a missing key throws an exception. You have to check if the key exists first:

if( !Session.ContainsKey("Member") )
    Session["Member"] = new Member();

Edit:

And to answer your question; you can create your own indexer or implement the IDictionary interface. This way you can avoid the default behavior of throwing an exception

simendsjo
It's a pity you can't hide the default indexer with an extension method.
Graphain
I don't agree. If you could hide methods with extension methods, you sure will have some hard to track down bugs. You wouldn't want a third party library to automatically change object.Equals for instance. Or change IDictionary behavior you might rely on.
simendsjo
That's an argument against inheritance as well then.
Graphain
@Graphain: It might... If you don't follow Liskov's substitution principle you will certainly get into problems using inheritance too. But just referencing a namespace and see code break in mysterious ways in runtime is worse as you have no control over it.
simendsjo
I guess, though this already happens to some degree - if you declare an extension method with the same name as a class your extension method won't be called, and extension methods can conflict when multiple assemblies declare the same name.
Graphain
@Graphin: "your extension method won't be called" - I couldn't reproduce this. Tried both the callers class and another. Works for me.. "can conflict when multiple assemblies declare the same name" - yes, but as long as it's a compile-time error it's ok. The problem of changed behavior at runtime is far greater.
simendsjo
"your extension method won't be called" - according to Microsoft the instance method is favoured over the extension method where a name conflict occurs.
Graphain
Ah.. You meant "with the same name as a _method_ in the class extended". In this case it's quite the same as I pointed out earlier. If the class method wasn't favoured, an extension method could hijack class methods. I'm a bit surprised it doesn't yield a warning though..
simendsjo
@Graphain: I asked a question regarding this: http://stackoverflow.com/questions/3116606/extension-methods-overridden-by-class-gives-no-warning
simendsjo
+1  A: 

If you want to do this inline:

dict["key"] = dict.ContainsKey("key") ? (dict["key"] ?? "object") : "object"; 

Alternatively:

dict["key"] = dict.ContainsKey("key") && dict["key"] == null ? dict["key"] : "object";

Edit as per comments:

public static TValue GetSafe(this Dictionary<TKey, TValue> thisDictionary, TKey key) 
{ 
   if (!thisDictionary.ContainsKey(key))
       thisDictionary[key] = null;

   return thisDictionary[key];
}

This allows you to do this:

thisDictionary["randomKey"] = thisDictionary.GetSafe("randomKey") ?? "someValue";
Graphain
well, it's kind of redundant if we do this... =(
Jronny
Depends on whether you're initialising object or referring to a variable. See my update for the alternative with pure ternary/no null-coalescing.
Graphain
This is different from what you asked. You asked can we assign to the dictionary using a null coallesce on whether the key is already set. Your solution is simply a way to set a default value for your specified key.
Graphain
@Jronny, you can set if the key is not contained.
Graphain
Should it be like dict["key"] = dict.ContainsKey("key") ? dict["key"] : "object";
Jronny
Thanks, I get it. The exception thrown there was on the getter. But can we not use null coalesce on that instead of ternary?
Jronny
Graphain
I was trying to write an indexer extension for the Dictionary class that could be able to return null when key is not found so we could use the null coalesce thingy, but it just did not work.
Jronny
If you want it to behave like Session and don't want to implement a custom Dictionary wrapper then you'll need to set each of the keys before you access them (i.e. if (!dict.ContainsKey("key")) { dict["key"] = null; }
Graphain
this way, checking for nulls as the values of the specific key, and nulls for the key not found exception would be both hit, thus we could use colesce.
Jronny
@Jronny you cant extend methods that already exist. You could do what I'm about to put in an answer though.
Graphain
Updated, I think this is what you want.
Graphain
Thanks. This is actually what I did before but I was too concerned with the elegance of the code that I was thinking of the impossible. =)
Jronny
Yeah it's the best way, you can't inject an indexer, you have to make your own dictionary.
Graphain