web-dev-qa-db-ja.com

Dictionary <bool ?, string>のキーとしてnullを使用できないのはなぜですか?

どうやら、キーがnull入力可能な型であっても、キーにnullを使用することはできません。

このコード:

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

...この例外が発生します:

値をnullにすることはできません。パラメーター名:キー

説明:現在の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

なぜ.NETフレームワークはキーにnull値を許可するタイプを許可するが、null値を許可しないのですか?

65
devuxer

Dictionary<SomeType, string>があり、SomeTypeが参照型で、nullをキーとして渡そうとした場合、同じことがわかります。nullのみに影響するものではありませんbool?のように入力します。 NULL可能かどうかに関係なく、任意のタイプをキーとして使用できます。

それはすべて、nullsを実際に比較できないという事実に帰着します。キーにnullを配置できない背後にあるロジックは、他のオブジェクトと比較するように設計されているプロパティは、null参照を比較することを一貫性のないものにすることです。

仕様から理由が必要な場合は、「キーはnull参照にできません」 MSDNで に要約されます。

可能な回避策の例が必要な場合は、 nullキーを許可するIDictionary実装が必要です に似たものを試すことができます。

35

多くの場合、C++の方法論と手法に戻って、.NET Frameworkが特定の方法で機能する方法と理由を完全に理解する必要があります。

C++では、使用されないキーを選択することがよくあります-辞書はこのキーを使用して、削除されたエントリや空のエントリを指します。たとえば、<int, int>の辞書があり、エントリを挿入した後、それを削除します。その場でガベージクリーンアップを実行するのではなく、辞書を再構築し、パフォーマンスが低下します。ディクショナリはKEY値を以前に選択したキーに置き換えるだけです。つまり、基本的には「ディクショナリのメモリスペースを走査するとき、この<key,value>ペアが存在しないふりをして、気軽に上書きしてください。

このようなキーは、特定の方法でバケット内のスペースを事前に割り当てる辞書でも使用されます。コンテンツが有効かどうかを示すフラグを各エントリに持つ代わりに、バケットを「初期化」するキーが必要です。したがって、トリプル<key, value, initialized>の代わりに、キー== empty_keyの場合は初期化されていないというルールを持つタプル<key, value>があります。したがって、empty_keyを有効なKEYとして使用することはできません値。

この種の動作は、Googleハッシュテーブル(.NETユーザー向けの辞書:)のドキュメントでご覧いただけます: http://google-sparsehash.googlecode.com/svn/trunk/doc/dense_hash_map.html

set_deleted_keyおよびset_empty_key関数を見て、私が話していることを理解してください。

.NETは、パフォーマンスを向上させるこの種の気の利いたトリックを行うために、一意のdeleted_keyまたはempty_keyのいずれかとしてNULLを使用します。

13

Null boolを使用できませんか? null許容型は参照型のように機能するためです。また、null参照を辞書キーとして使用することもできません。

辞書キーとしてnull参照を使用できない理由は、おそらくMicrosoftの設計上の決定にかかっています。 nullキーを許可するには、nullキーをチェックする必要があるため、実装が遅くなり、複雑になります。たとえば、実装では、null参照での.Equalsまたは.GetHashCodeの使用を避ける必要があります。

Nullキーを許可することは望ましいことですが、今は動作を変更するには遅すぎます。回避策が必要な場合は、許可されたnullキーを使用して独自の辞書を作成するか、暗黙的にTとの間で変換するラッパー構造体を作成し、辞書のキータイプを作成できます(つまり、構造体はnullと比較とハッシュを処理するので、辞書はnullを「見ない」。

4
Craig Gidney

基本的な理由はありません。 HashSetはnullを許可し、HashSetはキーが値と同じタイプの単なる辞書です。それで、本当に、nullキーは許可されるべきでしたが、今それを変更するために壊れているので、私たちはそれで立ち往生しています。

3

辞書(基本的な説明)
ディクショナリは、.NET framework 2.0で導入されたHashtableクラスの一般的な(型指定された)実装です。

ハッシュテーブルは、キー(より具体的にはキーのハッシュ)に基づいて値を保存します。
。NETのすべてのオブジェクトにはGetHashCodeメソッドがあります。
キー値のペアをハッシュテーブルに挿入すると、GetHashCodeがキーで呼び出されます。
考えてみてください:GetHashCodenullメソッドを呼び出すことはできません。

つまり、Nullable型はどうですか?
Nullableクラスは単なるラッパーであり、null値を値型に割り当てることができます。基本的に、ラッパーは、nullかどうかを示すHasValueブール値と、値型の値を含むValueで構成されます。

一緒に入れて何を得るのか
。NETは、ハッシュテーブル/ディクショナリでキーとして使用するものを実際に気にしません。
ただし、キーと値の組み合わせを追加するときは、キーのハッシュを生成できる必要があります。
値がNullable内にラップされているかどうかは関係ありません。null.GetHashCodeは不可能です。

ディクショナリのIndexerプロパティとAddメソッドは、nullをチェックし、nullを検出すると例外をスローします。

2
Zyphrax

MSDNページによると、nullを使用しないことは契約の一部です。 http://msdn.Microsoft.com/en-us/library/k7z0zy8k.aspx

理由は、有効な値としてnullを使用すると、理由もなくコードが複雑になるからだと思います。

1
Aryabhatta

ああ、一般的なコードの問題。 Reflectorを介してこのブロックを検討してください。

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

「Nullable nullは許可されますが、参照型をnullにすることはできません」と言うようにこのコードを書き換える方法はありません。

それでは、TKeyが「bool」にならないようにするにはどうすればよいでしょうか。繰り返しになりますが、C#言語にはそれを言うことができるものは何もありません。

0
Jonathan Allen

辞書は、特にGetHashCodeメソッドがないため、さまざまな理由でnull参照型を受け入れることができません。

ヌル値可能値タイプのヌル値は、ヌル値を表すことを意味します。セマンティクスは、可能な限り参照ヌルと同義であることを意味します。 null可能な値型の実装の詳細のためにnull参照を使用できない場合に、nullのnull可能な値を使用できる場合、少し奇妙になります。

それまたは辞書には次のものがあります:

if (key == null)

そして彼らはそれについて本当に考えなかった。

0
ICR

キー値は一意である必要があるため、nullはキーがないことを示すため、nullを有効なキーにすることはできません。

.Net framework does n't allow null valueで例外をスローする理由。

Nullableが許可され、コンパイル時にキャッチされない理由に関しては、Nullable以外のすべてのwhereを許可するT句は不可能だからだと思います(少なくともそれを達成する方法はわかりません)。

0