web-dev-qa-db-ja.com

いつnull可能型は例外をスローしますか?

次のコードを検討してください。

_int? x = null;
Console.Write ("Hashcode: ");
Console.WriteLine(x.GetHashCode());
Console.Write("Type: ");
Console.WriteLine(x.GetType());
_

実行されると、Hashcodeは_0_であると書き込みますが、NullReferenceExceptionのタイプを判別しようとしてxで失敗します。 nullable型で呼び出されるメソッドは、実際には基になる値で呼び出されることを知っているので、x.GetHashCode()の間にプログラムが失敗することを期待していました。

それでは、これら2つの方法の根本的な違いは何ですか?なぜ最初の方法が失敗しないのですか?

これは、int? x = null;が本質的に値タイプSystem.Nullable<int>のインスタンスを、「内部」null値で作成するためです(.HasVaueプロパティで確認できます)。 GetHashCodeが呼び出されると、オーバーライドNullable<int>.GetHashCodeがメソッド候補(メソッドが仮想であるため)になり、Nullable<int>のインスタンスを取得し、そのインスタンスメソッドを完全に実行します。

GetTypeを呼び出すと、メソッドは非仮想であるため、Nullable<int>のインスタンスは、 ドキュメント およびボックス化された値に従って、最初にSystem.Objectにボックス化されます。 nullであるため、NullReferenceExceptionです。

34
Cheng Chen

ダニー・チェンの正解を明確にするには:

  • Nullable<T>は値型です。値の型は、nullを示すブール(falseはnullを意味する)とT(値)で構成されます。
  • 他のすべての値型とは異なり、null許容型はボックス化されたNullable<T>にボックス化されません。ボックス化されたTまたはnull参照のいずれかにボックス化します。
  • 値型Sで実装されたメソッドは、見えないref S引数を持っているかのように実装されます。これがthisが渡される方法です。
  • 参照型Cによって実装されるメソッドは、見えないC引数が存在するかのように実装されます。これがthisが渡される方法です。
  • 興味深いケースは、参照ベースクラスで定義され、ベースクラスを継承する構造体によってオーバーライドされる仮想メソッドです。

これで、何が起こるかを推測するのに十分な情報が得られました。 GetHashCodeは仮想およびNullable<T>でオーバーライドされますですので、呼び出すときは、thisに不可視のref Nullable<T>引数があったかのように呼び出します。ボクシングは発生しません。

GetTypeは仮想ではないため、オーバーライドできず、objectで定義されます。そのため、objectに対してthisが必要です。Nullable<T>で呼び出された場合、レシーバーはボックス化する必要があり、そのためnullにボックス化できるため、スローできます。

((object)x).GetHashCode()を呼び出した場合、例外が表示されます。

16
Eric Lippert

Nullable<T>.GetHashCode()の実装は次のとおりです。

_public override int GetHashCode()
{
    if (!this.HasValue)
    {
        return 0;
    }
    return this.value.GetHashCode();
}
_

そのため、値がnullの場合、常に_0_が取得されます。

x.GetType()null.GetType()と同じで、_Object reference not set to an instance of an object_をスローします

5
Sunil

GetHashCodeがnullチェックを取得しているようです。 (JetBrainsを使用して定義を表示)

public override int GetHashCode()
{
  if (!this.hasValue)
    return 0;
  return this.value.GetHashCode();
}
1
Captain0