Nullable<br>を選択します.

9481 ワード

一、紹介
周知のように、値タイプ変数はnullできません.これも、値タイプと呼ばれる理由です.しかし,実際の開発ではnullの値を持つシーンも必要である.たとえば、次のようなシーンがあります.
シーン1:データベーステーブルから空の整数データ列を取得します.データベースのnull値はC#のInt 32タイプに割り当てることができません.
シーン2:UIでプロパティをバインドしますが、一部の値タイプのフィールドは入力する必要はありません(たとえば、ユーザ管理の死亡日).
シーン3:Javaでjava.Util.Dateは参照タイプなので、このタイプのフィールドをnullに設定できます.しかし、CLRではSystem.DateTimeは値タイプであり、DateTime変数はnullできません.Javaを使用して作成されたアプリケーションがCLR上で実行されるWebサービスに日付/時間を伝える場合、Javaアプリケーションの送信がnullの場合、CLRには対応するタイプがありません.
シーン4:関数で値タイプを渡す場合、パラメータの値が指定できず、渡したくない場合は、デフォルト値を使用します.しかし、デフォルト値は最適な選択ではない場合があります.デフォルト値は実際にデフォルトのパラメータ値を渡しているため、論理には特別な処理が必要です.
シーン5:xmlまたはjsonからデータを逆シーケンス化すると、データソースに値タイプ属性の値が不足し、処理が不便になります.
もちろん、私たちの日常の仕事には似たようなことがたくさんあります.
これらの状況から抜け出すために、MicrosoftはCLRに空の値タイプとして使用できる概念を追加した.この点をよりよく理解するためにSystemを見てみましょう.Nullableタイプの論理定義:
 1 namespace System 2 { 3     [Serializable] 4     public struct Nullable where T : struct  5     { 6         private bool hasValue; 7         internal T value; 8   9         public Nullable(T value) {10             this.value = value; 
11             this.hasValue = true;12         }13 14         public bool HasValue { 
15             get {16                 return hasValue; 
17             } 
18         }19  20         public T Value { 
21             get {22                 if (!HasValue) { 
23                     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue); 
24                 }25                 return value; 
26             }27         }28 29         public T GetValueOrDefault() { 
30             return value;31         } 
32 33         public T GetValueOrDefault(T defaultValue) {34             return HasValue ? value : defaultValue;35         } 
36 37         public override bool Equals(object other) { 
38             if (!HasValue) return other == null; 
39             if (other == null) return false;40             return value.Equals(other); 
41         }42 43         public override int GetHashCode() {44             return HasValue ? value.GetHashCode() : 0; 
45         }46  47         public override string ToString() { 
48             return HasValue ? value.ToString() : "";49         } 
50 51         public static implicit operator Nullable(T value) {52             return new Nullable(value);53         } 
54 55         public static explicit operator T(Nullable value) { 
56             return value.Value; 
57         }58     }59 }

上の定義から以下の点をまとめることができます.
  • Nullableタイプも値タイプです.
  • Nullableタイプには、ベース値を表すValue属性と、nullであるか否かを表すBooleanタイプのHasValue属性とが含まれる.
  • Nullableは軽量レベルの値タイプです.Nullableタイプのインスタンス占有メモリのサイズは、値タイプとBooleanタイプ占有メモリのサイズの和に等しい.
  • Nullableの汎用パラメータTは、値タイプでなければならない.Nullableタイプと値タイプを組み合わせてのみ使用できます.ユーザー定義の値タイプも使用できます.

  •  
    二、文法と使い方
    Nullableタイプを使用するには、他の値タイプの汎用パラメータTを1つ指定するだけです.
    例:
    1     Nullable i = 1;2     Nullable j = null;3     Nullable> k; //        ,     。

    CLRはまた、簡潔な方法を提供している.
    1     int? i = 1;2     int? j = null;

    ベースタイプの値は、Value属性で取得できます.以下に示すようにnullでない場合、実際の値が返されます.そうでない場合、InvalidOperationException異常が放出されます.Valueプロパティを呼び出すときにnullであるかどうかを確認する必要があります.
     1     Nullable i = 1; 2     Nullable j = null; 3  4     Console.WriteLine(i.HasValue); 5     //    :True 6  7     Console.WriteLine(i.Value); 8     //    :1 9 10     Console.WriteLine(j.HasValue);11     //    :False12 13     Console.WriteLine(j.Value);14     //   : System.InvalidOperationException

     
    三、タイプの変換と演算
    C#は、Nullableタイプを使用する簡単な構文もサポートします.また、Nullableインスタンスの暗黙的な変換と変換もサポートされています.次の例を示します.
     1     //  System.Int32     Nullable  2     int? i = 5; 3  4     //  'null'     Nullable  5     int? j = null; 6  7     //  Nullable Int32      8     int k = (int)i; 9 10     //          11     Double? x = 5; //  Int Nullable      12     Double? y = j; //  Nullable     Nullable

    Nullableタイプにはオペレータを使用し、含まれるベースタイプと同じ方法で使用します.
  • 一元演算子(++、--、-など)は、Nullableタイプ値がnullの場合nullを返します.
  • 二元演算子(+、-、*、/、%、^など)任意のオペランドはnullであり、nullを返します.
  • ==演算子の場合、両方のオペランドがnullの場合、式の計算結果はtrueであり、いずれかのオペランドがnullの場合、式の計算結果はfalseである.両方がnullでない場合は、通常通り比較されます.
  • リレーショナル演算子(>、=、<=)について、いずれかのオペランドがnullの場合、演算結果はfalseであり、オペランドがnullでない場合、この値を比較します.
  • 次の例を参照してください.
     1     int? i = 5; 2     int? j = null; 3  4     //       5     i++; // i = 6  6     j = -j; // j = null 7  8     //       9     i = i + 3; // i = 9 10     j = j * 3; // j = null;11 12     //      (==、!=)13     var r = i == null; //r = false14     r = j == null; //r = true15     r = i != j; //r = true16 17     //      (、<=、>=)18     r = i > j; //r = false19 20     i = null;21     r = i >= j; //r = false,  ,i=null、j=null,  >=      false

    Nullableは、参照タイプのように三元オペレータをサポートすることもできます.
    1     //          null(         ),    0. 2     int age = employee.Age ?? 0;3 4     //              。5     int?[] numbers = {};6     int total = numbers.Sum() ?? 0;

     
    四、梱包と解体
    Nullableは値のタイプであることはすでに知っていますが、今からその箱詰めと箱の取り外しについて話しましょう.CLRは、Nullableタイプの梱包と解体を処理するために特殊なルールを採用しています.Nullableタイプのインスタンスが箱詰めされると、CLRはインスタンスのHasValueプロパティをチェックします.trueの場合、インスタンスValueプロパティの値を箱詰めして結果を返します.falseを返すとnullに直接戻り、何の処理もしません.箱を分解して処理するときは、箱に入れるのと逆です.CLRは、ボックスを外すオブジェクトがnullであるかどうかをチェックし、新しいインスタンスnew Nullable()を直接作成し、nullでない場合はオブジェクトをタイプTにボックスを外し、新しいインスタンスnew Nullable(t)を作成します. 
     1     int? n = null; 2     object o = n; //        ,    null  3  4     Console.WriteLine("o is null = {0}", object.ReferenceEquals(o, null)); 5     //    :o is null = True 6  7  8     n = 5; 9     o = n; //o        Int3210 11     Console.WriteLine("o's type = {0}", o.GetType());12     //    :o's type = System.Int3213 14     o = 5;15 16     // Int32     Nullable  17     int? a = (Int32?)o; // a = 5 
    18     // Int32     Int32  19     int b = (Int32)o; // b = 520 21     //         null22     o = null;23     //  null  Nullable  24     a = (Int32?)o; // a = null 25     b = (Int32)o; //     :NullReferenceException

     
    五、GetType()方法
    NullableタイプのGetType()メソッドを呼び出すと、CLRは実際に汎用パラメータのタイプを返します.したがって、NullableインスタンスがInt 32タイプであるかNullableであるかを区別することはできません.次の例を参照してください.
    1     int? i = 10;2     Console.WriteLine(i.GetType());3     //     :System.Int324     5     i = null;6     Console.WriteLine(i.GetType()); //NullReferenceException

    原因分析:
    これは、GetType()メソッドを呼び出すと、現在のインスタンスが箱詰めされているためであり、前述の箱詰めと解体の内容に基づいて、ここで実際にはInt 32タイプのGetType()メソッドが呼び出されている.
    値タイプのGetType()メソッドを呼び出すと、いずれも箱詰めが発生します.この点については、自分で検証することができます.
     
    六、ToString()方法
    NullableタイプのToString()メソッドを呼び出すと、HasValue属性の値がfalseの場合、Stringが戻る.Empty、属性の値がtrueの場合、呼び出される論理はValue.ToString().次の例を参照してください.
    1     int? i = 10;2     Console.WriteLine(i.ToString());3     //    :104 5     i = null;6     Console.WriteLine(i.ToString() == string.Empty);7     //    :True

     
    七、システムNullableヘルプクラス
    マイクロソフトは同じ名前のSystemも提供しています.Nullableの静的クラスは、3つの方法で構成されています.
     1 public static class Nullable 2 { 3     //                。 4     public static Type GetUnderlyingType(Type nullableType); 5  6     //        System.Nullable   。 7     public static int Compare(T? n1, T? n2) where T : struct 8  9     //       System.Nullable       。10     public static bool Equals(T? n1, T? n2) where T : struct11 }

    ここではGetUnderlyingType(Type nullableType)の方法を重点的に説明し、他の2つの方法は値を比較するために使用されているので、自分で研究することができます.
    GetUnderlyingType(Type nullableType)メソッドは、nullableTypeパラメータが閉じたNullable汎用でない場合、nullを返すために空のタイプを返すために使用されます. 
     1     Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable))); 2     //    :System.Int32 3  4     Console.WriteLine(Nullable.GetUnderlyingType(typeof(Nullable<>)) == null); 5     //    :True 6  7     Console.WriteLine(Nullable.GetUnderlyingType(typeof(int)) == null); 8     //    :True 9 10     Console.WriteLine(Nullable.GetUnderlyingType(typeof(string)) == null);11     //    :True

      
    八、文法糖
    マイクロソフトはNullableに豊富な文法糖を提供して開発者の仕事量を減らしています.以下は私が参考にしたいことです.
    簡潔に書く
    コンパイルされた文
     1     int? i = 5; 2  3     int? j = null; 4  5     var r = i != null; 6  7     var v = (int) i; 8  9     i++;10 11     i = i + 3;12 13     r = i != j;14 15     r = i >= j;16 17     var k = i + j;18 19     double? x = 5;20     21     double? y = j;
     1     int? i = new int?(5); 2  3     int? j = new int?(); 4  5     var r = i.HasValue; 6  7     var v = i.Value; 8  9     i = i.HasValue ? new int?(i.GetValueOrDefault() + 1) : new int?();10 11     i = i.HasValue ? new int?(i.GetValueOrDefault() + 3) : new int?();12 13     r = i.GetValueOrDefault() != j.GetValueOrDefault() || i.HasValue != j.HasValue;14 15     r = i.GetValueOrDefault() >= j.GetValueOrDefault() && i.HasValue & j.HasValue;16 17     int? k = i.HasValue & j.HasValue ? new int?(i.GetValueOrDefault() + j.GetValueOrDefault()) : new int?();18 19     double? x = new double?((double) 5);20     21     double? y = j.HasValue ? new double?((double) j.GetValueOrDefault()) : new double?();