The documentation states that bool Dictionary<TKey, TValue>.ContainsKey(TKey key)
throws an exception in case a null key is passed. Could anyone give a reason for that? Wouldn't it be more practical if it just returned false
?
Why does Dictionary.ContainsKey throw ArgumentNullException? [closed]
Asked Answered
If ContainsKey(null)
returned false
it would give the misleading impression that null keys are allowed..
And it probably also helps to locate your mistake sooner - since there's no way you should use a null reference in ContainsKey, you know there's a problem, rather than just getting a "nope, it's not here, keep trying". Yeah, I think I like it this way better :) –
Angieangil
This is how it is implemented: (Source)
public bool ContainsKey(TKey key) {
return FindEntry(key) >= 0;
}
And the method FindEntry
as:
private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
Since having a null
value as key in dictionary is not allowed.
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add(null, 10);
The above would produce an exception:
Value cannot be null. Parameter name: key
For your question:
Wouldn't it be more practical if it just returned false?
Someone from Microsoft could probably reply that. But IMO, since adding a null
value for key is not allowed there is no point in checking for null
key in ContainsKey
I expect the sole reason is probably that while
IEquatable<T>.GetHashCode(T)
could have been specified to return a constant when given a null value, it wasn't. If a Dictionary
's call to IEquatable<T>
is going to throw an exception when given a null argument, it would be better to have the exception thrown from Dictionary
, giving the name of its parameter, than from IEquatable<T>.GetHashCode()
. –
Isoleucine © 2022 - 2024 — McMap. All rights reserved.
null.GetHashCode()
? – QuietismIEqualityComparer
says it is =p. It's actually entirely viable to choose to write a dictionary that accepts null keys, the library just didn't choose to do that. – Instinctiveif (key == null) throw new ArgumentNullExceltion;
doif (key == null) return false;
– Pointenoireif (key == null) ...
. You can do the comparison, but it doesn't make any sense for value types. – AngieangilIEqualityComparer
and let it either return a value or throw an exception. My guess is they needed to cast the key toobject
and then compare it to null in order to be able to add such a check, given that, as you say, the key can be a value type. – Instinctivenull
keys makes sense if your design philosophy is thatnull
represents a kind of invalid value (like, well, a null pointer), rather than a valid bottom type. I guess that's the general philosophy of the .NET framework. – OsseticT
must either be a reference type, in which case it may be directly compared tonull
, a nullable type, in which case comparison tonull
can be "compared" tonull
using aHasValue
check, or a structure type in which case a widening conversion will exist toT?
, which may then be "compared" tonull
. To actually box the key if it's a non-nullable value type would be a little silly; it would seem either the JIT should optimize that away or aNullChecker<T>.IsNull
method should be able to expedite the test. – Isoleucineint test = 31; if (test == null) ...
). However, the IL code still plainly states that there's abox
instruction. The JIT will quite certainly optimize it away for reference types, but it's there in the IL. The IL code for the generic version is different than the one you get when the type is known (and no, it doesn't explicitly mention the Nullable at all, but that's due to the various Nullable optimization in the C# compiler). – AngieangilIEquatable<T>
wouldn't be necessary (given generic value-type variablest1
andt2
and objecto1
, if one has performed o1 = t1;, then
t2.Equals(o1)` won't be much slower than using theIEquatable
override as `t2.Equals(t1). – Isoleucine