Почему вы не можете использовать null в качестве ключа для словаря?
видимо, вы не можете использовать null
для ключа, даже если ваш ключ имеет тип nullable.
этот код:
var nullableBoolLabels = new System.Collections.Generic.Dictionary<bool?, string>
{
{ true, "Yes" },
{ false, "No" },
{ null, "(n/a)" }
};
...результаты в этом исключении:
значение не может быть null. Имя параметра: 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
зачем .Чистая основа позволяют тип, допускающий значение null для ключа, но не допускает значение null?
11 ответов:
он сказал бы вам то же самое, если бы у вас был
Dictionary<SomeType, string>
,SomeType
будучи ссылочным типом, и вы пытались передатьnull
как ключ, это не то, что влияет только на нулевой тип, какbool?
. Вы можете использовать любой тип в качестве ключа, nullable или нет.все сводится к тому, что вы не можете действительно сравнить
nulls
. Я предполагаю, что логика не в состоянии поставитьnull
в ключе свойство, которое предназначено для сравнения с другими объектами, заключается в том, что оно делает его несвязно сравниватьnull
ссылки.Если вам нужна причина из спецификаций, она сводится к "ключ не может быть нулевой ссылкой" на MSDN.
если вы хотите пример возможного обходного пути, вы можете попробовать что-то похожее на нужна реализация IDictionary, которая позволит нулевой ключ
часто вам приходится возвращаться к методологиям и методам C++, чтобы полностью понять, как и почему .NET Framework работает определенным образом.
в C++, вы часто должны выбрать ключ, который не будет использоваться - словарь использует этот ключ для указания на удаленные и/или пустые записи. Например, у вас есть словарь
<int, int>
, и после вставки записи, вы удалите его. Вместо того, чтобы запускать очистку мусора прямо тогда и там, реструктуризация словаря, и приводит к плохой производительности; словарь просто заменит значение ключа на ключ, который вы ранее выбрали, в основном это означает "когда вы пересекаете пространство памяти словаря, притворитесь это<key,value>
пара не существует, не стесняйтесь ее перезаписывать."такой ключ также используется в словарях, которые предварительно выделяют пространство в ведрах определенным образом - вам нужен ключ для "инициализации" ведер вместо того, чтобы иметь флаг для каждой записи, который указывает, является ли его содержимое действительно. Так что вместо того, чтобы иметь тройной
<key, value, initialized>
у вас будет Кортеж<key, value>
С правилом, что если ключ = = empty_key, то он не был инициализирован - и поэтому вы не можете использовать empty_key в качестве допустимого значения ключа.вы можете увидеть такое поведение в Google hashtable (словарь для вас .NET people :) в документации здесь:http://google-sparsehash.googlecode.com/svn/trunk/doc/dense_hash_map.html
посмотреть
set_deleted_key
иset_empty_key
функции, чтобы получить то, о чем я говорю.Я бы поставил .NET использует NULL как уникальный deleted_key или empty_key для того, чтобы делать такие изящные трюки, которые повышают производительность.
вы не можете использовать нулевой bool? потому что nullable типы предназначены для работы как ссылочные типы. Вы также не можете использовать нулевую ссылку в качестве ключа словаря.
причина, по которой вы не можете использовать нулевую ссылку в качестве ключа словаря, вероятно, сводится к проектному решению в Microsoft. Разрешение нулевых ключей требует их проверки, что делает реализацию более медленной и сложной. Например, реализация должна была бы избегать использования .Равно или .GetHashCode на null ссылка.
Я согласен, что разрешение нулевых ключей было бы предпочтительнее, но сейчас слишком поздно менять поведение. Если вам нужен обходной путь, вы можете написать свой собственный словарь с разрешенными пустыми ключами, или вы можете написать структуру-оболочку, которая неявно преобразуется в/из T и делает это ключевым типом для вашего словаря (т. е. эта структура будет обернуть null и ручкой сравнивать и хэширования, так что словарь никогда не увидит нуль).
нет никакой фундаментальной причины. HashSet позволяет null, а HashSet-это просто словарь, где ключ имеет тот же тип, что и значение. Итак, на самом деле, нулевые ключи должны были быть разрешены, но теперь они будут ломаться, чтобы изменить его, поэтому мы застряли с ним.
Dictionairies (основное описание)
Словарь-это универсальная (типизированная) реализация класса Hashtable, представленная в .NET framework 2.0.в хэш-таблице хранится значение, основанное на ключе (более конкретно хэш ключа).
Каждый объект в .NET имеет методGetHashCode
.
Когда вы вставляете пару значений ключа в хэш-таблицу,GetHashCode
вызывается на ключ.
Подумайте об этом: вы не можете назватьGetHashCode
методnull
.так что насчет типов с нулевым значением?
ЭлементNullable
класс-это просто оболочка, позволяющая присваивать нулевые значения типам значений. В основном обертка состоит изHasValue
логическое значение, которое говорит, если он имеет значение null или нет, иValue
чтобы содержать значение типа значения.сложите вместе и что вы получите
.NET действительно не волнует, что вы используете в качестве ключа в хеш-таблице/словаре.
Но когда вы добавляете комбинация значений ключа, она должна иметь возможность генерировать хэш ключа.
Это не имеет значения, если ваше значение завернуто вNullable
, null.GetHashCode невозможно.индексатор свойство и добавить методы словаря будет проверять на null, и бросить исключение, когда он находит null.
Не использование null является частью контракта согласно странице MSDN:http://msdn.microsoft.com/en-us/library/k7z0zy8k.aspx
Я думаю, причина в том, что наличие null в качестве допустимого значения просто усложнит код без причины.
значение ключа должно быть уникальным, поэтому null не может быть допустимым ключом, потому что null указывает на отсутствие ключа.
вот почему .Net framework не разрешить null значение и бросить исключение.
что касается того, почему Nullable разрешено, а не ловит во время компиляции, я думаю, причина в том, что
where
предложение, которое позволяет everytT
кроме Nullable один не представляется возможным (по крайней мере, я не знаю, как этого добиться).
Ах, проблемы общего кода. Рассмотрим этот блок через рефлектор:
private void Insert(TKey key, TValue value, bool add) { int freeList; if (key == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key); }
нет никакого способа, чтобы переписать этот код, чтобы сказать "допускает значение null значение null разрешено, но не ссылочные типы, чтобы быть null".
хорошо, так как ab не позволяет TKey быть " bool?". Ну опять же, в языке C# нет ничего, что позволило бы вам это сказать.
словари не могут принимать нулевые ссылочные типы по разным причинам, не в последнюю очередь потому, что у них нет метода GetHashCode.
нулевое значение для типа nullable value предназначено для представления нулевого значения-семантика должна быть как можно более синонимичной ссылочному нулю. Было бы немного странно, если вы могли бы использовать null значение, допускающее значение null, когда вы не могли использовать пустые ссылки только потому, что деталь реализации, поддерживающим значение null типы.
либо это, либо словарь имеет:
if (key == null)
и они никогда не думали об этом.
ключи словаря не могут быть null в .NET, независимо от типа ключа (nullable или иначе).
из MSDN: пока объект используется в качестве ключа в словаре)>), он не должен изменяться каким-либо образом, который влияет на его хэш-значение. Каждый ключ в словаре)>) должен быть уникальным в соответствии с компаратором равенства словаря. Ключ не может быть пустой ссылкой (nothing в Visual Basic), то это значение может быть, если тип TValue значение ссылочный тип. (http://msdn.microsoft.com/en-us/library/xfhwa508.aspx)
Я только что читал об этом; и как ответил Эрик, теперь я считаю, что это неверно, не все строки автоматически интернируются, и мне нужно было переопределить операцию равенства.
Это бит меня, когда я преобразовал словарь из использования строки в качестве ключа к массиву байтов.
Я был в мышлении vanilla C строки, просто являющейся массивом символов, поэтому мне потребовалось некоторое время, чтобы понять, почему строка, построенная конкатенацией, работала как ключ для поиска в то время как массив байтов, построенный в цикле, не сделал.
это потому, что внутренне .net присваивает все строки, содержащие одно и то же значение для одной и той же ссылки. (это называется "интернирование")
Итак, после запуска:
{ string str1 = "AB"; string str2 = "A"; str1 += "C"; str2 += "BC"; }
str1 и str2 фактически указывают на одно и то же место в памяти! что делает их одинаковыми onject; что позволяет словарю найти элемент, добавленный с помощью str1 в качестве ключа, используя str2.
в то время как если ты:
{ 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 для его поиска.