简体   繁体   中英

Should I check whether particular key is present in Dictionary before accessing it?

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?

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.)

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 .

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.

我个人会检查密钥是否在那里,不管你是否确定它是,有些人可能会说这个检查是多余的,而且这个字典会引发一个你可以捕获的异常,但你不应该依赖那个例外,你应该检查自己,然后抛出你自己的异常,这意味着某个东西或结果对象带有成功标志和内部原因...失败机制实际上是依赖于实现的。

Surely the answer is "it all depends on the situation". You need to balance the risk that the key will be missing from the dictionary (low for small systems where there is limited access to the data, where you can rely on the order things are done, larger for larger systems, multiple programmers accessing the same data, especially with read/write/delete access, where threads are involved and order cannot be guaranteed or where data originates externally and reading can fail) with the impact of the risk (safety-critical systems, commercial releases or systems that a business will rely on compared with something made for fun, for a one-off job and/or for your use only) and with any requirements for speed, size and laziness.

If I were making a system to control railway signalling I would want to be safe against all possible and impossible errors, and safe from errors in the error-handling and so on (Murphy's 2nd law: "what can't go wrong will go wrong".) If I'm chucking stuff together for fun, even if size and speed are not an issue I will be MUCH more relaxed about stuff like this - I will want to get to the fun stuff.

Of course, sometimes this is the fun stuff in itself.

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.

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

1) Avoid exceptions where possible, as exceptions are expensive - ie 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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM