For example:
.method private hidebysig instance void Insert(!TKey key, !TValue 'value', bool add) cil managed
{
.maxstack 3
.locals init (
[0] int32 num,
[1] int32 num2,
[2] int32 num3,
[3] int32 num4)
L_0000: ldarg.1
L_0001: box !TKey
L_0006: brtrue.s L_000e
L_0008: ldc.i4.5
L_0009: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument)
This is from the internal Add method of Dictionary<int,Object> in .NET 4.0. Although generics are widely touted as helping to avoid boxing of value types, why is this system component doing this inefficient check on every operation for a value-type key? If I understand correctly, this not only hurts performance, but will always return true as well (a boxed value type will never be a null reference)
edit: Summary of Marc's answer for this specific question: the reason it matters is that this Dictionary<K,V> implementation has opted to disallow the use of "null" Nullable<T> instances as keys. Because the MSIL box instruction gives special treatment to the Nullable<T> value type, the check is not necessarily futile开发者_运维问答 for all value types.
A Nullable<T>
is a struct
/ value-type, and can be null
(depending on your definition of null
; but certainly it can box to null
). And not all TKey
are value-type (string
being perhaps the most common TKey
).
There is a requirement here that the key isn't null; so it does need to verify that.
In reality, boxing isn't as bad as people think; even boxed, it will be gen-0 collected. It could special-case via generics (like EqualityComparer<T>
does - via a few different sub-classes), but that seems overkill.
The JIT may also be able to remove the null-check. I say may here, as although this is often cited, I have seen cases where the null-check was beyond the JIT's ability to remove.
精彩评论