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

显然,即使你的密钥是一个可为空的types,你也不能使用null作为密钥。

此代码:

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

…导致这个例外:

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

说明:执行当前Web请求期间发生未处理的exception。 请查看堆栈跟踪,了解有关错误的更多信息以及源代码的位置。

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

为什么.NET框架允许一个键为空的types,但不允许空值?

它会告诉你同样的事情,如果你有一个Dictionary<SomeType, string>SomeType是一个引用types,你试图传递null作为关键,它不是只影响像bool?空types的东西bool? 。 您可以使用任何types的键,可以为空或不可以。

这一切都归结于你不能真正比较nulls的事实。 我假设不能在关键字中放置null的逻辑,被devise为与其他对象进行比较的属性是它使得它不一致以比较null引用。

如果你需要一个规范的原因,它归结为MSDN上的“一个键不能是一个空引用”。

如果你想要一个可能的解决方法的例子,你可以尝试类似于需要一个IDictionary的实现,将允许一个空键

通常,您必须回到C ++的方法和技术上,才能完全理解.NET Framework如何以及为什么以特定的方式工作。

在C ++中,你经常需要select一个不会被使用的键 – 字典使用这个键指向删除和/或空的条目。 例如,你有一个<int, int>的字典,并在插入一个条目后,删除它。 而不是在那里运行垃圾清理,重build字典,导致性能不佳; 字典将用你之前select的键代替KEY值,基本上意思是“当你遍历字典memoryspace时,假装这个<key,value>对不存在,随意覆盖它。

这样的密钥也用于以特定方式预先分配桶中空间的字典 – 您需要一个密钥来“初始化”桶,而不是每个条目都有一个标志,指示其内容是否有效。 因此,不是有一个三元<key, value, initialized>你将有一个元组<key, value> ,规则是如果key == empty_key那么它没有被初始化 – 因此你不能使用empty_key作为有效的KEY值。

您可以在Google文档中查看Google散列表中的这种行为(您的.NET人员的词典): http : //google-sparsehash.googlecode.com/svn/trunk/doc/dense_hash_map.html

看看set_deleted_keyset_empty_key函数来得到我在说的。

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

你不能使用null布尔? 因为可空types意味着像引用types一样。 您也不能使用空引用作为字典键。

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

我同意允许空键是可取的,但现在改变行为为时已晚。 如果你需要一个解决方法,你可以使用允许的空键来编写你自己的字典,或者你可以编写一个包装结构,它隐式地转换为/从T,并使你的字典的键types(即结构将包装null和处理比较和哈希,所以字典从不“看到”空)。

词典(基本描述)
字典是在.NET Framework 2.0中引入的Hashtable类的通用(types)实现。

散列表存储基于密钥(更具体地,密钥的散列)的值。
.NET中的每个对象都有GetHashCode方法。
将键值对插入散列表中时,将在键上调用GetHashCode
想一想:你不能在null上调用GetHashCode方法。

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

把它放在一起,你得到什么
.NET并不关心你用作散列表/字典中的关键字。
但是,当您添加一个键值组合时,它必须能够生成密钥的散列。
不要紧,如果你的值是包装在一个Nullable ,null.GetHashCode是不可能的。

Dictionary的Indexer属性和Add方法将检查null,并在发现null时抛出exception。

没有根本的原因。 HashSet允许为null,而HashSet只是一个字典,其中的键与该值的types相同。 所以,真的,应该允许null键,但现在打破改变,所以我们坚持下去。

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

我想原因是有null作为有效的价值将只是复杂的代码无缘无故。

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

那为什么.Net框架不允许值和抛出exception。

至于为什么Nullable允许,而不是在编译时捕获,我认为原因是因为where子句允许每个T除Nullable之外是不可能的(至less我不知道如何实现这一点)。

啊,通用代码的问题。 考虑这个块通过reflection器:

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

没有办法重写这段代码,说“可以使用空值空值,但不允许引用types为空”。

好吧,那么怎样才不让TKey成为“布尔”呢? 再说一遍,C#语言中没有什么可以让你这么说的。

由于各种原因,字典不能接受空引用types,这不仅仅是因为它们没有GetHashCode方法。

可为null的值types的空值意味着表示空值 – 语义意味着尽可能与引用null同义。 如果你可以使用空的空值,那么你可能会有点奇怪,因为你不能使用null引用,只是因为可以为null的值types的实现细节。

无论是或字典有:

 if (key == null) 

他们从来没有真正想过。

无论密钥的types(可为空还是以其他值),字典密钥在.NET中都不能为空。

从MSDN开始:只要一个对象被用作Dictionary <(Of <(TKey,TValue>)>)中的一个键,它就不能以任何影响其散列值的方式进行改变。 Dictionary <(Of <(TKey,TValue>)>)中的每个键必须根据字典的相等比较器是唯一的。 一个键不能为空引用(在Visual Basic中为Nothing),但是值可以是,如果值typesTValue是引用types。 ( http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

我刚刚读到这个; 正如Eric所回答的,我现在认为这是不正确的,并不是所有的string都会自动被Interned,我需要重写相等的操作。


当我使用一个string作为关键字来转换一个字典到一个字节数组的时候,这让我感到困惑。

我只是一个简单的字符数组的string的香草C心态,所以我花了一段时间,以找出为什么一个串联构build的string作为查找的关键,而一个循环内置的字节数组没有。

这是因为.NET内部将所有包含相同值的string分配给同一个引用。 (这就是所谓的“国际”)

所以,运行后:

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

str1和str2实际上指向了内存中完全相同的地方! 这使他们相同的目标; 这允许字典通过使用str2来查找添加了str1的项目作为关键字。

而如果你:

 { 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和char2是不同的引用; 如果使用char1将项目添加到字典中,则不能使用char2来查找它。