C# 为什么不能使用 null 作为 Dictionary<bool?, string> 的键?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2174692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:08:03  来源:igfitidea点击:

Why can't you use null as a key for a Dictionary<bool?, string>?

c#.netdictionaryhashtable

提问by devuxer

Apparently, you cannot use a nullfor a key, even if your key is a nullable type.

显然,您不能将 anull用于键,即使您的键是可空类型。

This code:

这段代码:

var nullableBoolLabels = new System.Collections.Generic.Dictionary<bool?, string>
{
    { true, "Yes" },
    { false, "No" },
    { null, "(n/a)" }
};

...results in this exception:

...导致此异常:

Value cannot be null. Parameter name: key

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

[ArgumentNullException: Value cannot be null. Parameter name: key]System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) +44System.Collections.Generic.Dictionary'2.Insert(TKey key, TValue value, Boolean add) +40
System.Collections.Generic.Dictionary'2.Add(TKey key, TValue value) +13

值不能为空。参数名称:key

说明:在执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其起源于代码的更多信息。

[ArgumentNullException: Value cannot be null. Parameter name: key]System.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument) +44System.Collections.Generic.Dictionary'2.Insert(TKey key, TValue value, Boolean add) +40
System.Collections.Generic.Dictionary'2.Add(TKey key, TValue value) +13

Why would the .NET framework allow a nullable type for a key, but not allow a null value?

为什么 .NET 框架允许键的可空类型,但不允许空值?

采纳答案by Dynami Le Savard

It would tell you the same thing if you had a Dictionary<SomeType, string>, SomeTypebeing a reference type, and you tried to pass nullas the key, it is not something affecting only nullable type like bool?. You can use any type as the key, nullable or not.

它会告诉你同样的事情,如果你有一个Dictionary<SomeType, string>SomeType是一个引用类型,而你试图通过null为关键,它不是只影响可空类型一样bool?。您可以使用任何类型作为键,无论是否可以为空。

It all comes down to the fact that you can't really compare nulls. I assume the logic behind not being able to put nullin the key, a property that is designed to be compared with other objects is that it makes it incoherent to compare nullreferences.

这一切都归结为你无法真正比​​较的事实nulls。我假设无法放入null键背后的逻辑,一个旨在与其他对象进行比较的属性是它使得比较null引用变得不连贯。

If you want a reason from the specs, it boils down to a "A key cannot be a null reference " on MSDN.

如果您想从规范中找到原因,可以归结为MSDN 上的“键不能是空引用” 。

If you want an exemple of a possible workaround, you can try something similar to Need an IDictionary implementation that will allow a null key

如果您想要一个可能的解决方法的示例,您可以尝试类似于Need an IDictionary implementation that will allow a null key

回答by jeffora

Dictionary keys can't be null in .NET, regardless of the type of the key (nullable or otherwise).

.NET 中的字典键不能为空,无论键的类型如何(可为空或其他类型)。

From MSDN: As long as an object is used as a key in the Dictionary<(Of <(TKey, TValue>)>), it must not change in any way that affects its hash value. Every key in a Dictionary<(Of <(TKey, TValue>)>) must be unique according to the dictionary's equality comparer. A key cannot be null reference (Nothing in Visual Basic), but a value can be, if the value type TValue is a reference type. (http://msdn.microsoft.com/en-us/library/xfhwa508.aspx)

来自 MSDN:只要对象在 Dictionary<(Of <(TKey, TValue>)>) 中用作键,它就不能以任何影响其哈希值的方式更改。根据字典的相等比较器, Dictionary<(Of <(TKey, TValue>)>) 中的每个键都必须是唯一的。键不能是空引用(在 Visual Basic 中为 Nothing),但如果值类型 TValue 是引用类型,则值可以是空引用。( http://msdn.microsoft.com/en-us/library/xfhwa508.aspx)

回答by Fitzchak Yitzchaki

Key value must to be unique, so null cannot be a valid key because null indicate no key.

键值必须是唯一的,因此 null 不能是有效键,因为 null 表示没有键。

That why .Net framework doesn'tallow nullvalue and throw an exception.

这就是为什么Net框架不会允许值,并抛出一个异常。

As far as why Nullable allowed, and not catches at compile time, I think the reason is because that whereclause that allow everyt Texcept Nullable one is not possible (at least I don't know how to achieve that).

至于为什么允许 Nullable 而不是在编译时捕获,我认为原因是因为where允许T除 Nullable 之外的所有内容的子句是不可能的(至少我不知道如何实现)。

回答by Dynami Le Savard

Not using null is part of the contract according to the MSDN page: http://msdn.microsoft.com/en-us/library/k7z0zy8k.aspx

根据 MSDN 页面,不使用 null 是合同的一部分:http: //msdn.microsoft.com/en-us/library/k7z0zy8k.aspx

I guess the reason is that having null as valid value will just complicate the code for no reason.

我想原因是将 null 作为有效值只会无缘无故地使代码复杂化。

回答by Jonathan Allen

Ah, the problems of generic code. Consider this block via Reflector:

啊,泛型代码的问题。通过反射器考虑这个块:

private void Insert(TKey key, TValue value, bool add)
{
    int freeList;
    if (key == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }

There is no way to rewrite this code to say "Nullable nulls are allowed, but don't allow reference types to be null".

无法重写此代码以说明“允许可空的空值,但不允许引用类型为空值”。

Ok, so how ab out not allowing TKey to be a "bool?". Well again, there is nothing in the C# language that would allow you to say that.

好的,那么不允许 TKey 成为“布尔值”怎么样?再说一次,C# 语言中没有任何内容可以让您这么说。

回答by Mahmoud Al-Qudsi

Oftentimes you have to go back to C++ methodologies and techniques to fully understand how and why the .NET Framework works in a particular manner.

通常,您必须回到 C++ 方法论和技术来完全理解 .NET Framework 以特定方式工作的方式和原因。

In C++, you oftentimes have to pick a key that will not be used - the dictionary uses this key to point to deleted and/or empty entries. For instance, you have a dictionary of <int, int>, and after inserting an entry, you delete it. Rather than running the Garbage Cleanup right then and there, restructuring the dictionary, and leading to bad performance; the dictionary will just replace the KEY value with the key you previously selected, basically meaning "when you're traversing the dictionary memoryspace, pretend this <key,value>pair does not exist, feel free to overwrite it."

在 C++ 中,您经常需要选择一个不会被使用的键——字典使用这个键来指向已删除和/或空的条目。例如,您有一个 字典<int, int>,在插入一个条目后,您将其删除。而不是立即运行垃圾清理,重构字典,并导致性能不佳;字典只会用你之前选择的键替换 KEY 值,基本上意思是“当你遍历字典内存空间时,假装这<key,value>对不存在,随意覆盖它。”

Such a key is also used in dictionaries that pre-allocate space in buckets in a particular manner - you need a key to "initialize" the buckets with instead of having a flag for each entry that indicates whether or not its contents are valid. So instead of having a triple <key, value, initialized>you would have a tuple <key, value>with the rule being that if key == empty_key then it hasn't been initialized - and therefore you may not use empty_key as a valid KEY value.

这样的键也用于以特定方式在存储桶中预先分配空间的字典中 - 您需要一个键来“初始化”存储桶,而不是为每个条目设置一个标志来指示其内容是否有效。因此,<key, value, initialized>您将拥有一个元组<key, value>,而不是三元组,规则是如果 key == empty_key 则它尚未初始化 - 因此您不能将 empty_key 用作有效的 KEY 值。

You can see this sort of behavior in the Google hashtable (dictionary for you .NET people :) in the documentation here: http://google-sparsehash.googlecode.com/svn/trunk/doc/dense_hash_map.html

您可以在此处的文档中的 Google 哈希表(.NET 人员的字典 :) 中看到这种行为:http: //google-sparsehash.googlecode.com/svn/trunk/doc/dense_hash_map.html

Look at the set_deleted_keyand set_empty_keyfunctions to get what I'm talking about.

查看set_deleted_keyset_empty_key函数以了解我在说什么。

I'd wager .NET uses NULL as either the unique deleted_key or empty_key in order to do these sort of nifty tricks that improve performance.

我打赌.NET 使用NULL 作为唯一的deleted_key 或empty_key 以执行这些提高性能的漂亮技巧。

回答by Craig Gidney

You can't use a null bool? because nullable types are meant to act like reference types. You can't use a null reference as a dictionary key, either.

你不能使用空布尔值?因为可空类型旨在充当引用类型。您也不能使用空引用作为字典键。

The reason you can't use a null reference as a dictionary key probably comes down to a design decision at Microsoft. Allowing null keys requires checking for them, which makes the implementation slower and more complicated. For example, the implementation would have to avoid using .Equals or .GetHashCode on a null reference.

不能使用空引用作为字典键的原因可能归结为 Microsoft 的设计决策。允许空键需要检查它们,这使得实现更慢和更复杂。例如,实现必须避免在空引用上使用 .Equals 或 .GetHashCode。

I agree that allowing null keys would be preferable, but it's too late to change the behavior now. If you need a workaround you can write your own dictionary with allowed null keys, or you could write a wrapper struct which implicitly converts to/from T and make that the key-type for your dictionary (ie. the struct would wrap the null and handle comparing and hashing, so the dictionary never 'sees' the null).

我同意允许空键更可取,但现在改变行为为时已晚。如果您需要一种解决方法,您可以使用允许的空键编写自己的字典,或者您可以编写一个包装结构,该结构隐式转换为/从 T 并使其成为您字典的键类型(即该结构将包装空和处理比较和散列,因此字典永远不会“看到”空值)。

回答by ICR

Dictionaries can't accept null reference types for a variety of reasons, not least because they don't have a GetHashCode method.

由于各种原因,字典不能接受空引用类型,尤其是因为它们没有 GetHashCode 方法。

A null value for nullable value type is meant to represent a null value – the semantics are meant to be as synonymous with a reference null as possible. It would be slightly odd if you could use null nullable value where you couldn't use null references just because of an implementation detail of nullable value types.

可空值类型的空值意味着表示空值——语义意味着尽可能与引用空值同义。如果您可以使用 null 可空值,而不能仅因为可空值类型的实现细节而使用 null 引用,那将有点奇怪。

Either that or Dictionary has:

那个或字典有:

if (key == null)

and they never really thought about it.

他们从来没有真正考虑过。

回答by John Muller

I've just been reading up on this; and as Eric replied, I now believe this is incorrect, not all strings are automatically Interned, and I needed to override the equality operation.

我刚刚阅读了这个;正如 Eric 回答的那样,我现在认为这是不正确的,并非所有字符串都会自动被 Interned,我需要覆盖相等操作。



This bit me when I converted a dictionary from using a string as a key to an array of bytes.

当我将字典从使用字符串作为键转换为字节数组时,这让我很不舒服。

I was in the vanilla C mindset of a string simply being an array of characters, so it took me a while to figure out why a string built by concatenation worked as a key for lookups while a byte array built in a loop did not.

我处于一个字符串只是一个字符数组的普通 C 思维模式中,所以我花了一段时间才弄清楚为什么通过连接构建的字符串可以作为查找的键,而在循环中构建的字节数组却没有。

It's because internally .net assigns all strings that contain the same value to the same reference. (it's called 'Interning')

这是因为 .net 在内部将所有包含相同值的字符串分配给相同的引用。(这被称为“实习”)

so, after running:

所以,运行后:

{
string str1 = "AB";
string str2 = "A";
str1 += "C";
str2 += "BC";
}

str1 and str2 actually point to the exact same place in memory! which makes them the same onject; which allows a dictionary to find an item added with str1 as a key by using str2.

str1 和 str2 实际上指向内存中完全相同的位置!这使它们成为相同的对象;它允许字典使用 str2 查找添加了 str1 作为键的项目。

while if you:

而如果你:

{
char[3] char1;
char[3] char2;
char1[0] = 'A';
char1[1] = 'B';
char1[2] = 'C';
char2[0] = 'A';
char2[1] = 'B';
char2[2] = 'C';
}

char1 and char2 are distinct references; if you use char1 to add an item to a dictionary, you cannot use char2 to look it up.

char1 和 char2 是不同的引用;如果您使用 char1 将一个项目添加到字典中,则不能使用 char2 来查找它。

回答by Zyphrax

Dictionairies (basic description)
A dictionary is the generic (typed) implementation of the Hashtable class, introduced in .NET framework 2.0.

Dictionairies(基本描述)
字典是 Hashtable 类的通用(类型化)实现,在 .NET framework 2.0 中引入。

A hashtable stores a value based on a key (more specificly a hash of the key).
Every object in .NET has the method GetHashCode.
When you insert an key value pair into an hashtable, GetHashCodeis called on the key.
Think of it: you can't call the GetHashCodemethod on null.

哈希表存储基于键(更具体地说是键的哈希)的值。
.NET 中的每个对象都有方法GetHashCode.
当您将键值对插入哈希表时,GetHashCode会在键上调用。
想想看:你不能在GetHashCode上调用方法null

So what about Nullable types?
The Nullableclass is simply a wrapper, to allow null values to be assigned to value types. Basically the wrapper consists of an HasValueboolean that tells if it is null or not, and a Valueto contain the value of the value type.

那么 Nullable 类型呢?
Nullable类只是一个包装,以允许空值将被分配给值类型。基本上包装器由一个HasValue布尔值组成,它告诉它是否为空,以及一个Value包含值类型的值。

Put it together and what do you get
.NET doesn't really care what you use as a key in a hashtable/dictionary.
But when you add a key value combination, it has to be able to generate a hash of the key.
It doesn't matter if your value is wrapped inside a Nullable, null.GetHashCode is impossible.

把它放在一起,你会得到什么
.NET 并不真正关心你在哈希表/字典中使用什么作为键。
但是当您添加键值组合时,它必须能够生成键的哈希值。
如果您的值包含在 a 中并不重要Nullable, null.GetHashCode 是不可能的。

The Indexer property and Add methods of the Dictionary will check for null, and throw an exception when it finds null.

Dictionary 的 Indexer 属性和 Add 方法将检查是否为空,并在发现为空时抛出异常。