views:

510

answers:

8

Should I check whether particular key is present in Dictionary if I am sure it will be added in dictionary by the time I reach the code to access it?

There are two ways I can access the value in dictionary

  1. checking ContainsKey method. If it returns true then I access using indexer [key] of dictionary object.

or

  1. TryGetValue which will return true or false as well as return value through out parameter.

(2nd will perform better than 1st if I want to get value. Benchmark.)

However if I am sure that the function which is accessing global dictionary will surely have the key then should I still check using TryGetValue or without checking I should use indexer[].

Or I should never assume that and always check?

+5  A: 

If the dictionary is global (static/shared), you should be synchronizing access to it (this is important; otherwise you can corrupt it).

Even if your thread is only reading data, it needs to respect the locks of other threads that might be editing it.

However; if you are sure that the item is there, the indexer should be fine:

Foo foo;
lock(syncLock) {
    foo = data[key];
}
// use foo...

Otherwise, a useful pattern is to check and add in the same lock:

Foo foo;
lock(syncLock) {
    if(!data.TryGetValue(key, out foo)) {
        foo = new Foo(key);
        data.Add(key, foo);
    }
}
// use foo...

Here we only add the item if it wasn't there... but inside the same lock.

Marc Gravell
Nice, but I don't think that was the question? I don't see any reference to concurrency? :)
Thorarin
"global dictionary" - just set me down a given path. If my understanding is incorrect the OP can just ignore this reply ;-p
Marc Gravell
+2  A: 

Always check. Never say never. I assume your application is not that performance critical that you will have to save the checking time.

TIP: If you decide not to check, at least use Debug.Assert( dict.ContainsKey( key ) ); This will only be compiled when in Debug mode, your release build will not contain it. That way you could at least have the check when debugging.

Still: if possible, just check it :-)

EDIT: There have been some misconceptions here. By "always check" I did not only mean using an if somewhere. Handling an exception properly was also included in this. So, to be more precise: never take anything for granted, expect the unexpected. Check by ContainsKey or handle the potential exception, but do SOMETHING in case the element is not contained.

galaktor
I disagree with this. If the key is meant to be present and isn't, the right behaviour is to throw an exception, yes? And that's exactly what the indexer does - so why not use that behaviour? Additionally, I'm not keen on changing behaviour between debug and release builds... it sounds like a recipe for problems to me.
Jon Skeet
Yes, the Debug check was not intended to be best practice. BUT it is better than not checking at all. And throwing an exception if the data should be in the dictionary sure is the right way to go, but then it probably not be an internal exception from Dictionary, but one that better fits the actual problem (e. g. InvalidOperationException).
galaktor
I agree with Jon, you only check when it may or may not be there, if you are certain that it should be there then the correct path is to allow the exception to be thrown and handle it accordingly.
James
The original question did not say anything about handling excpetions. I understood that it's just about accessing the index and then hoping for the best. By "always check" I also included catching exceptions and acting appropriatley. I will edit the post and refine my statement: prepare for the unexpected.
galaktor
You definitely shouldn't be *catching* exceptions - use TryGetValue rather than accessing with an indexer and catching the exception. However, when the key should be there, using the indexer can give a *significant* improvement in readability over explicitly testing and throwing a different exception.
Jon Skeet
All I am saying is do SOMETHING. He wanted to know if he should just access the index and hope for god helping him. I said "no, check it". So where's the problem? I also think that the method itself should handle the KeyNotFoundException and rethrow a custom or more general wrapping exception one around it. And if readability was a practical reason to risk instability, then I must have misunderstood something.
galaktor
How is letting the exception propagate "risking instability"? Catching KeyNotFoundException is definitely a grottier approach than using TryGetValue, but unless I *really* want to throw a custom exception type, I'd just let the KeyNotFoundException propagate.
Jon Skeet
This really depends on the calling code, but I for sure would not expect having to catch a KeyNotFoundException at some level higher than maybe a specific collection implementation. Letting it propagate into my high level code would eventually mean risking to have an exception show to the user that could easily have been prevented from getting that far, which is what I meant by risking instability.
galaktor
Btw, my example in the previous comment "specific collection implementation" is a bad one. I just meant any code that directly accesses a collection like this. Also, if data is so important to an application as it is in this question (since it MUST be there), then I definately would not let it's abscence be described by a KeyNotFoundException, more something like a MyReallyImportantDataIsMissingException, or at least (as I already said) an InvalidOperationException.
galaktor
+10  A: 

Use the indexer if the key is meant to be present - if it's not present, it will throw an appropriate exception, which is the right behaviour if the absence of the key indicates a bug.

If it's valid for the key not to be present, use TryGetValue instead and react accordingly.

(Also apply Marc's advice about accessing a shared dictionary safely.)

Jon Skeet
A missing key is often an indication of another problem. If you know what the problem is and can provide a more useful message, then you should consider throwing your own exception since the message accompanying `KeyNotFoundException` is difficult to narrow down at times.
280Z28
It would certainly be useful if KeyNotFoundException gave the key as well - but other than that, just the stack trace is normally enough for me. How far would you take this: checking every possible null reference (not just arguments, but things you internally expect to be non-null) for nullity and throwing a custom exception on each one of those? There will always be exceptions thrown by bits of the framework in cases of internal inconsistency: if you try to anticipate every single one of them, the code ends up being more about the error cases than making progress.
Jon Skeet
A: 

TryGetValue is the same code as indexing it by key, except the former returns a default value (for the out parameter) where the latter throws an exception. Use TryGetValue and you'll get consistent checks with absolutely no performance loss.

Edit: As Jon said, if you know it will always have the key, then you can index it and let it throw the appropriate exception. However, if you can provide better context information by throwing it yourself with a detailed message, that would be preferable.

280Z28
A: 

There's 2 trains of thought on this from a performance point of view.

1) Avoid exceptions where possible, as exceptions are expensive - i.e. check before you try to retrieve a specific key from the dictionary, whether it exists or not. Better approach in my opinion if there's a fair chance it may not exist. This would prevent fairly common exceptions.

2) If you're confident the item will exist in there 99% of the time, then don't check for it's existence before accessing it. The 1% of times when it doesn't exist, an exception will be thrown but you've saved time for the other 99% of the time by not checking.

What I'm saying is, optimise for the majority if there is a clear one. If there is any real degree in uncertainty about an item existing, then check before retrieving.

AdaTheDev
I don't think this should really be about performance at all. It should be about correctness: if the key is *expected* to be there in correct situations, i.e. it's a bug for the key not to be there, then throwing an exception is the right approach to failure. Otherwise, you should detect that situation *without* using an exception - handling non-exceptional cases with exceptions is a bad idea, IMO.
Jon Skeet
I'm thinking more from the scenario where it not existing in the dictionary should not result in a failure e.g. should use a default value instead, or should add it in if it doesn't exist. The case where it should exist in the dictionary, but it's not a real bug if it doesn't exist - i.e. suppose the dictionary is a cache of some data from a DB, say the DB is unavailable and the cache fails to be populated, but you don't want that to cause a failure in the system. Difficult to get across a real-world example, so hope I'm making some kind of sense!
AdaTheDev
A: 

If you know that the dictionary normally contains the key, you don't have to check for it before accessing it.

If something would be wrong and the dictionary doesn't contain the items that it should, you can let the dictionary throw the exception. The only reason for checking for the key first would be if you want to take care of this problem situation yourself without getting the exception. Letting the dictionary throw the exception and catch that is however a perfectly valid way of handling the situation.

Guffa
A: 

I think Marc and Jon have it (as usual) pretty sown up. Since you also mention performance in your question it might be worth considering how you lock the dictionary.

The straightforward lock serialises all read access which may not be desirable if read is massively frequent and writes are relatively few. In that case using a ReaderWriterLockSlim might be better. The downside is the code is a little more complex and writes are slightly slower.

AnthonyWJones
+1  A: 

Personally I'd check the key is there, regardless of whether or not you are SURE it is, some may say this check is superfluous and that dictionary will throw an exception which you can catch, but imho you should not rely on that exception, you should check yourself and then either throw your own exception which means something or a result object with a success flag and reason inside... the failure mechanism is really implementation dependant.

krystan honour