呼び出しによって発生した例外は、何らかの理由で呼び出され、null 参照でメソッドを呼び出していますdefault(object).ToString()
。一方、参照ではないため、null 参照ではありません。nullと同等の値を持つ値型です。NullReferenceException
default(int?)
大きな実用的なポイントは、これが行われた場合、次のことが失敗するということです:
default(int?).HasValue // should return false, or throw an exception?
また、nullables と non-nullables を混在させる機能を台無しにしてしまいます。
((int?)null).Equals(1) // should return false, or throw an exception?
そして、以下は完全に役に立たなくなります:
default(int?).GetValueOrDefault(-1);
null を削除して null との比較を強制することもできHasValue
ますが、null 可能にされた値型の等値オーバーライドが、場合によっては null と比較して true を返すことができるとしたらどうなるでしょうか。それは素晴らしいアイデアではないかもしれませんが、それは可能であり、言語は対処しなければなりません.
null 許容型が導入された理由を振り返ってみましょう。参照型が null になる可能性は、非 null 可能性を強制する努力が払われない限り、参照型の概念に固有のものです。参照型は、何かを参照する型であり、何も参照しない可能性を意味します。nullと呼びます。
多くの場合は面倒ですが、「不明な値」、「有効な値がない」などを表すなど、さまざまな場合にこれを利用できます (たとえば、データベースでnullが意味するものに使用できます)。 .
この時点で、特定の参照がどのオブジェクトも参照しないという単純な事実を超えて、特定のコンテキストでnullに意味を与えました。
これは便利なので、int
orDateTime
を null に設定することもできますが、これらは他の何かを参照する型ではないため、これ以上何も参照しない状態になることはできません。哺乳類である私は、羽を失うことがあります。
2.0 で導入された null 許容型は、参照型とは異なるメカニズムを通じて、セマンティックnullを持つことができる値型の形式を提供します。このほとんどは、存在しない場合は自分でコーディングできますが、特別なボクシングおよびプロモーション ルールにより、より賢明なボクシングおよびオペレーターの使用が可能になります。
わかった。NullReferenceExceptions
それでは、そもそもなぜ起こるのかを考えてみましょう。2 つは避けられず、1 つは C# の設計上の決定です (.NET 全体には当てはまりません)。
- 仮想メソッドまたはプロパティを呼び出すか、null 参照のフィールドにアクセスしようとしています。どのオーバーライドを呼び出す必要があるかを調べる方法がなく、そのようなフィールドがないため、これは失敗する必要があります。
- 仮想メソッドまたはプロパティを呼び出すか、フィールドにアクセスする null 参照で非仮想メソッドまたはプロパティを呼び出します。これは明らかにポイント 1 のバリアントですが、次の設計上の決定には、途中で失敗するのではなく、最初にこれが失敗することを保証するという利点があります (混乱を招き、長期的な副作用が生じる可能性があります)。 .
- 仮想メソッドまたはプロパティを呼び出さない null 参照で非仮想メソッドまたはプロパティを呼び出すか、フィールドにアクセスします。これが許可されるべきではない固有の理由はなく、一部の言語では許可されていますが、C#では、一貫性のためにa を強制するの
callvirt
ではなく使用することにしました (私は同意するとは言えませんが、そこまでです)。call
NullReferenceException
これらのケースはいずれも、null 許容値型にはまったく適用されません。アクセスするフィールドまたはメソッドのオーバーライドを知る方法がない状態に、null 許容値型を配置することはできません。justの全体的な概念は、NullReferenceException
ここでは意味がありません。
全体として、 a をスローしないことは、null 参照が使用されている場合に限り、他の型NullReferenceException
と一貫性があります。
null nullable-type を呼び出すと がスローされる場合があることに注意してください。これは、が仮想ではないGetType()
ためGetType()
です。値型で呼び出されると、常に暗黙のボクシングが行われます。これは他の値タイプにも当てはまります。
(1).GetType()
は次のように扱われます。
((object)1).GetType()
ただし、null 許容型の場合、ボクシングは falseHasValue
を持つものを null に変換するため、次のようになります。
default(int?).GetType()
次のように扱われます。
((object)default(int?)).GetType()
その結果GetType()
、null オブジェクトで呼び出されるため、スローされます。
ついでに言うと、これはなぜ偽物NullReferenceType
がより賢明な設計上の決定であったのかということを私たちにもたらします-その行動を必要とする人々はいつでも箱詰めすることができます. それを通過させたい場合((object)myNullableValue).GetString()
は、例外を強制するために言語がそれを特別なケースとして扱う必要がないように使用してください。
編集
ああ、背後にある仕組みについて言及するのを忘れていましたNullReferenceException
。
のテストNullReferenceException
は非常に安価です。ほとんどの場合、問題を無視し、発生した場合に OS から例外をキャッチするためです。つまり、テストはありません。
null参照例外の発生/生成の背後にある CLR 実装とは何ですか? を参照してください。そして、どれも null 許容値型では機能しないことに注意してください。