6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

.NETのnull許容値型(Nullable<T>)でハマったこと

Posted at

データベースの型クラスを自動生成する過程でハマったのでメモ。

現象

  • 実行時にint?(Nullable<int>)となっている変数に対して、型情報を取得するとintが返ってくる

原因

Nullable<T>型がボックス化されるとき、

  • HasValuefalseならばnull
  • HasValuetrueならばT型に

自動でボックス化される。
そのため実行時の型を調べようとobject.GetType()を呼ぶと、T型が返ってくるか、nullにボックス化されるため、NullReferenceExceptionの例外が発生する。

対象の型がNullableかどうか検証するには、Nullable.GetUnderlyingTypeを使うこと。

検証コード

Try.NETに以下のコードを貼り付けることで、オンラインで検証できます。

Sample.cs
using System;

public class Program
{
  public static void Main()
  {
    // typeof(int?)はNullable<int>を返す
    Console.WriteLine($"typeof(int?) = {typeof(int?)}");

    int? nullableThree = 3;
    int? nullValue = null;
    int notNullTen = 10;

    // Nullable<T>.GetType()は未定義のため、object.GetType()が呼ばれる。(ボックス化)
    // その際HasValue == trueの場合はT型に、falseの場合はnullにボックス化される。(Nullable<int>にはボックス化されない)

    // int型にボックス化されるため、intを返す
    Console.WriteLine($"nullableThree type is {nullableThree.GetType()}");

    try
    {
      // nullにボックス化されるため、NullReferenceExceptionの例外発生
      Console.WriteLine(nullValue.GetType());
    }
    catch (NullReferenceException)
    {
      Console.WriteLine("nullValue causes NullReferenceException");
    }
    
    // is演算子も同様。n is intとn is int?は同じ結果を返す(Nullable<int>に変えても同様)
    Console.WriteLine($"nullableThree is int = {nullableThree is int}");
    Console.WriteLine($"nullableThree is int? = {nullableThree is int?}");
    Console.WriteLine($"nullValue is int = {nullValue is int}");
    Console.WriteLine($"nullValue is int? = {nullValue is int?}");
    // int型の変数に対するn is int?に至っては「常にtrueを返すけどいい?」という注釈が出る
    Console.WriteLine($"notNullTen is int? = {notNullTen is int?}");

    // Nullable<int>とintの区別にはNullable.GetUnderlyingType(System名前空間)を使用する
    Console.WriteLine($"IsNullableType(notNullTen) = {IsNullableType(notNullTen)}");
    Console.WriteLine($"IsNullableType(nullableThree) = {IsNullableType(nullableThree)}");
    
    // リフレクションでプロパティやフィールドの型を取得した場合はNullable<T>になる
    Console.WriteLine(typeof(Foo).GetProperty(nameof(Foo.Value)).PropertyType);
  }

  public class Foo
  {
    public int? Value { get; set; }
  }

  public static bool IsNullableType<T>(T o) => Nullable.GetUnderlyingType(typeof(T)) != null;

}
実行結果
typeof(int?) = System.Nullable`1[System.Int32]
nullableThree type is System.Int32
nullValue causes NullReferenceException
nullableThree is int = True
nullableThree is int? = True
nullValue is int = False
nullValue is int? = False
notNullTen is int? = True
IsNullableType(notNullTen) = False
IsNullableType(nullableThree) = True
System.Nullable`1[System.Int32]

参考

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?