次の方法で共有


8 種類

8.1 全般

C# 言語の型は、参照型値型の 2 つの主要なカテゴリに分けられます。 値型と参照型はどちらも、1 つ以上の型パラメータを取るジェネリック型になる場合があります。 型パラメータでは、値型と参照型の両方を指定できます。

type
    : reference_type
    | value_type
    | type_parameter
    | pointer_type     // unsafe code support
    ;

pointer_type (§23.3) は、未使用のコード (§23) のみで利用できます。

値型は参照型とは異なり、値型の変数にはデータが直接格納されるのに対し、参照型の変数にはデータへの参照が格納され、後者はオブジェクトと呼ばれます。 参照型を使用すると 2 つの変数が同じオブジェクトを参照できるため、1 つの変数に対する演算によって、もう一方の変数によって参照されるオブジェクトに影響を与えることができます。 値型を使用すると、各々の変数がデータのコピーを各々で持ち、1 つに対する操作がもう一方に影響を与えることはできません。

: 変数が参照または出力パラメータである場合、変数自体にはストレージがなく、別の変数のストレージを参照します。 この場合、ref または out 変数は実質的に別の変数のエイリアスであり、別個の変数ではありません。 注釈

C# の型システムは統一されており、あらゆる型の値をオブジェクトとして扱うことができます。 C# における型はすべて、直接的または間接的に object クラス型から派生し、object はすべての型の究極の基底クラスです。 参照型の値は、値を単純に object 型としてみなすことによってオブジェクトとして扱われます。 値型の値は、ボックス化とボックス化解除操作を実行することによって、オブジェクトとして扱われます (§8.3.13)。

便宜上、この仕様全体では、一部のライブラリ タイプ名は完全な名前修飾を使用せずに記述されています。 詳細については§C.5 を参照してください。

8.2 参照型

8.2.1 全般

参照型は、クラス型、インターフェイス型、配列型、デリゲート型、または dynamic 型です。 それぞれの null 非許容参照型には、型名に ? を追加することで示される対応する null 許容参照型があります。

reference_type
    : non_nullable_reference_type
    | nullable_reference_type
    ;

non_nullable_reference_type
    : class_type
    | interface_type
    | array_type
    | delegate_type
    | 'dynamic'
    ;

class_type
    : type_name
    | 'object'
    | 'string'
    ;

interface_type
    : type_name
    ;

array_type
    : non_array_type rank_specifier+
    ;

non_array_type
    : value_type
    | class_type
    | interface_type
    | delegate_type
    | 'dynamic'
    | type_parameter
    | pointer_type      // unsafe code support
    ;

rank_specifier
    : '[' ','* ']'
    ;

delegate_type
    : type_name
    ;

nullable_reference_type
    : non_nullable_reference_type nullable_type_annotation
    ;

nullable_type_annotation
    : '?'
    ;

pointer_type は、未使用のコード (§23.3) のみで利用できます。 nullable_reference_typeは、§8.9 で後述します。

参照型の値は、型のインスタンスへの参照であり、後者はオブジェクトと呼ばれます。 特別な値 null はすべての参照型と互換性があり、インスタンスが存在しないことを示します。

8.2.2 クラス型

クラス型は、データ メンバー (定数とフィールド)、関数メンバー (メソッド、プロパティ、イベント、インデクサ、演算子、インスタンス コンストラクタ、ファイナライザ、および静的コンストラクタ)、およびネストされた型を含むデータ構造を定義します。 クラス型は継承をサポートします。継承とは、派生クラスが基本クラスを拡張および特殊化できるメカニズムです。 クラス型のインスタンスは、object_creation_expressions (§12.8.17.2) を使用して作成されます。

クラス型については §15 で説明します。

特定の定義済みクラス型は、次の表に示すように、C# 言語では特別な意味を持ちます。

クラス型 説明
System.Object その他すべての型の最終的な基底クラス。 「§8.2.3」を参照してください。
System.String C# 言語の文字列型。 「§8.2.5」を参照してください。
System.ValueType すべての値型の基底クラス。 「§8.3.2」を参照してください。
System.Enum すべての enum 型の基底クラス。 「§19.5」を参照してください。
System.Array すべての配列型の基底クラス。 「§17.2.2」を参照してください。
System.Delegate すべての delegate 型の基底クラス。 「§20.1」を参照してください。
System.Exception すべての例外型の基底クラス。 「§21.3」を参照してください。

8.2.3 オブジェクト型

object クラス型は、その他すべての型の最終的な基底クラスです。 C# のすべての型は、直接的または間接的に object クラス型から派生します。

キーワード object は、定義済みクラス System.Object の単なるエイリアスです。

8.2.4 動的型

dynamic などの object 型は、オブジェクトを参照できます。 型 dynamic の式に演算が適用されると、その解決はプログラムが実行されるまで延期されます。 したがって、参照先のオブジェクトに操作を正当に適用できない場合、コンパイル中にエラーは発生しません。 代わりに、実行時に操作の解決が失敗すると例外がスローされます。

dynamic 型については §8.7 で、動的バインディングについては §12.3.1 で詳述しています。

8.2.5 文字列型

string 型は、object から直接継承される sealed クラス型です。 string クラスのインスタンスは Unicode 文字列を表します。

string 型の値は、文字列リテラルとして記述できます (§6.4.5.6)。

キーワード string は、定義済みクラス System.String の単なるエイリアスです。

8.2.6 インターフェイス型

インターフェイスによりコントラクトが定義されます。 インターフェイスを実装するクラスまたは構造体は、その契約に従う必要があります。 インターフェイスは複数の基本インターフェイスから継承することができ、クラスまたは構造体は複数のインターフェイスを実装することができます。

インターフェイス型については、§18 で説明します。

8.2.7 配列型

配列は、計算されたインデックス経由でアクセスされる 0 個以上の変数を含むデータ構造です。 配列に含まれる変数は配列の要素とも呼ばれ、すべて同じ型であり、この型は配列の要素型と呼ばれます。

配列型については、§17 で説明します。

8.2.8 デリゲート型

デリゲートは、1 つ以上のメソッドを参照するデータ構造です。 インスタンス メソッドの場合、対応するオブジェクト インスタンスも参照します。

: C または C++ のデリゲートに最も近いものは関数ポインターですが、関数ポインターは静的関数のみを参照できるのに対し、デリゲートは静的メソッドとインスタンス メソッドの両方を参照できます。 後者の場合、デリゲートはメソッドのエントリ ポイントへの参照だけでなく、メソッドを呼び出すオブジェクト インスタンスへの参照も格納します。 注釈

デリゲート型については、§20 で説明します。

8.3 値型

8.3.1 全般

値型は、構造体型または列挙型のいずれかです。 C# には、単純型と呼ばれる定義済みの構造型のセットが用意されています。 単純型はキーワードによって識別されます。

value_type
    : non_nullable_value_type
    | nullable_value_type
    ;

non_nullable_value_type
    : struct_type
    | enum_type
    ;

struct_type
    : type_name
    | simple_type
    | tuple_type
    ;

simple_type
    : numeric_type
    | 'bool'
    ;

numeric_type
    : integral_type
    | floating_point_type
    | 'decimal'
    ;

integral_type
    : 'sbyte'
    | 'byte'
    | 'short'
    | 'ushort'
    | 'int'
    | 'uint'
    | 'long'
    | 'ulong'
    | 'char'
    ;

floating_point_type
    : 'float'
    | 'double'
    ;

tuple_type
    : '(' tuple_type_element (',' tuple_type_element)+ ')'
    ;
    
tuple_type_element
    : type identifier?
    ;
    
enum_type
    : type_name
    ;

nullable_value_type
    : non_nullable_value_type nullable_type_annotation
    ;

参照型の変数とは異なり、値型の変数は、値型が null 許容値型である場合にのみ値 null を含めることができます (§8.3.12)。 すべての null 非許容値型には、同じ値セットと値 null を表す対応する null 許容値型が存在します。

値型の変数に割り当てると、割り当てられる値のコピーが作成されます。 これは、参照型の変数への代入とは異なります。参照型の変数への代入では、参照はコピーされますが、参照によって識別されるオブジェクトはコピーされません。

8.3.2 System.ValueType 型

すべての値型は、クラス class から継承される System.ValueTypeobject から暗黙的に継承され。 いかなる型も値型から派生することはできないため、値型は暗黙的にシールドされます (§15.2.2.3)。

System.ValueType 自体は、value_type ではないのでご注意ください。 むしろ、これはすべての value_type が自動的に派生される class_type です。

8.3.3 デフォルト コンストラクタ

すべての値型は、デフォルト コンストラクタと呼ばれる、パラメーターなしのパブリック インスタンス コンストラクタを暗黙的に宣言します。 デフォルト コンストラクタは、値型のデフォルト値と呼ばれるゼロ初期化インスタンスを返します。

  • すべての simple_types の場合、デフォルト値はすべてゼロのビット パターンによって生成された値です。
    • sbytebyteshortushortintuintlongulong の場合、デフォルト値は、0 です。
    • char の場合、デフォルト値は、'\x0000' です。
    • float の場合、デフォルト値は、0.0f です。
    • double の場合、デフォルト値は、0.0d です。
    • decimal の場合、デフォルト値は、0m です (つまり、スケール 0 の値 0)。
    • bool の場合、デフォルト値は、false です。
    • enum_typeE の場合、デフォルト値は、0 で、型 E に変換されます。
  • struct_type の場合、デフォルト値は、すべての値型フィールドをデフォルト値に設定し、すべての参照型フィールドを null に設定することによって生成される値です。
  • nullable_value_type の場合、デフォルト値はHasValue プロパティが false であるインスタンスです。 そのデフォルト値は、null 許容値型の null 値とも呼ばれます。 このような値の Value プロパティを読み取ろうとするとSystem.InvalidOperationException、型の例外がスローされます (§8.3.12)。

他のインスタンス コンストラクタと同様に、値型のデフォルト コンストラクタは new 演算子を使用して呼び出されます。

: 効率上の理由から、この要件は実装で実際にコンストラクタ呼び出しを生成することを意図していません。 値型の場合、デフォルト値式 (§12.8.21) は、デフォルト コンストラクタを使用する場合と同じ結果を生成します。 注釈

: 以下のコードでは、変数 ijk は、ゼロに初期化されます。

class A
{
    void F()
    {
        int i = 0;
        int j = new int();
        int k = default(int);
    }
}

終了サンプル

すべての値型には暗黙的にパブリックのパラメータなしのインスタンス コンストラクタがあるため、構造体型にパラメータなしのコンストラクタの明示的な宣言を含めることはできません。 ただし、構造体型では、パラメータ化されたインスタンス コンストラクタを宣言できます (§16.4.9)。

8.3.4 構造体型

構造体型は、定数、フィールド、メソッド、プロパティ、イベント、インデクサ、演算子、インスタンス コンストラクタ、静的コンストラクタ、およびネストされた型を宣言できる値型です。 構造体の宣言については §16 で説明されています。

8.3.5 単純型

C# には、単純型と呼ばれる定義済みの struct 型のセットが用意されています。 単純型はキーワードを使用して識別されますが、次の表に示すように、これらのキーワードは struct 名前空間の定義済み System 型の単なるエイリアスです。

キーワード エイリアス化された型
sbyte System.SByte
byte System.Byte
short System.Int16
ushort System.UInt16
int System.Int32
uint System.UInt32
long System.Int64
ulong System.UInt64
char System.Char
float System.Single
double System.Double
bool System.Boolean
decimal System.Decimal

単純型は構造体型のエイリアスとなるため、すべての単純型にはメンバーが存在します。

: int には System.Int32 で宣言されたメンバーと System.Object から継承されたメンバーがあり、次のステートメントが許可されます。

int i = int.MaxValue;      // System.Int32.MaxValue constant
string s = i.ToString();   // System.Int32.ToString() instance method
string t = 123.ToString(); // System.Int32.ToString() instance method

終了サンプル

: 単純型は、ある追加の操作を許可している点で、他の構造体型とは異なります。

  • ほとんどの単純型では、リテラルを記述することによって値を作成できます (§6.4.5)。ただし、C# では一般に構造体のリテラルについては規定されていません。 : 123 は、型 int のリテラルで、'a' は、char のリテラルです。 終了サンプル
  • 式のオペランドがすべて単純型定数である場合、コンパイラはコンパイル時に式を評価できます。 このような式は constant_expression (§12.23) と呼ばれます。 他の構造体型で定義された演算子を含む式は定数式とはみなされません。
  • const 宣言を通じて、単純型の定数を宣言することができます (§15.4)。 他の構造体型の定数を持つことはできませんが、静的読み取り専用フィールドによって同様の効果が提供されます。
  • 単純型に関係する変換は、他の構造体型によって定義された変換演算子の評価に参加できますが、ユーザー定義の変換演算子は、別のユーザー定義の変換演算子の評価に参加することはできません (§10.5.3)。

注釈

8.3.6 整数型

C# は、sbytebyteshortushortintuintlongulongchar の 9 つの整数型をサポートしています 整数型には次のサイズと値の範囲があります。

  • sbyte 型は、-128 から 127 までの値を持つ符号付き 8 ビット整数を表します。
  • byte 型は、0 から 255 までの値を持つ符号なし 8 ビット整数を表します。
  • short 型は、-32768 から 32767 までの値を持つ符号付き 16 ビット整数を表します。
  • ushort 型は、0 から 65535 までの値を持つ符号なし 16 ビット整数を表します。
  • int 型は、-2147483648 から 2147483647 までの値を持つ符号付き 32 ビット整数を表します。
  • uint 型は、0 から 4294967295 までの値を持つ符号なし 32 ビット整数を表します。
  • long 型は、-9223372036854775808 から 9223372036854775807 までの値を持つ符号付き 64 ビット整数を表します。
  • ulong 型は、0 から 18446744073709551615 までの値を持つ符号なし 64 ビット整数を表します。
  • char 型は、0 から 65535 までの値を持つ符号なし 16 ビット整数を表します。 char 型の可能な値のセットは、Unicode 文字セットに対応します。

    : charushort と同じ表現ですが、一方の型で許可されているすべての操作がもう一方の型でも許可されるわけではありません。 注釈

すべての符号付き整数型は 2 の補数形式を使用して表されます。

integral_type の単項演算子と二項演算子は、§12.4.7 で詳述されているように、常に符号付き 32 ビット精度、符号なし 32 ビット精度、符号付き 64 ビット精度、または符号なし 64 ビット精度で動作します。

char 型は整数型として分類されますが、他の整数型とは 2 つの点で異なります。

  • 他の型から char 型への定義済みの暗黙的な変換はありません。 特に、byte 型と ushort 型は char 型を使用して完全に表現可能な値の範囲を持っていますが、sbyte、byte、または ushort から char への暗黙的な変換は存在しません。
  • char 型の定数は、character_literal として記述するか、char 型へのキャストと組み合わせて integer_literal として記述する必要があります。

: (char)10 は、'\x000A' と同じです。 終了サンプル

checked および unchecked 演算子は、整数型の算術演算および変換に対するオーバーフロー チェックを制御するために使用します (§12.8.20)。 checked コンテキストでは、オーバーフローによりコンパイル時エラーが生成されるか、System.OverflowException がスローされます。 unchecked コンテキストでは、オーバーフローは無視され、宛先タイプに適合しない上位ビットは破棄されます。

8.3.7 浮動小数点型

C# は、floatdouble の 2 つの浮動小数点型をサポートします。 float および double 型は、32 ビット単精度および 64 ビット倍精度の IEC 60559 形式を使用して表され、次の値のセットが提供されます。

  • 正のゼロと負のゼロ。 ほとんどの場合、正のゼロと負のゼロは単純なゼロの値と同じように動作しますが、特定の操作ではこの2つを区別します (§12.10.3)。
  • 正の無限大と負の無限大。 無限大は、ゼロ以外の数字をゼロで割るような演算で生成されます。

    : 1.0 / 0.0 は正の無限大、–1.0 / 0.0 は負の無限大です。 終了サンプル

  • Not-a-Number (数字ではない) 値は、NaN と略されることがあります。 NaN は、ゼロをゼロで割るなど、無効な浮動小数点演算で生成されます。
  • s × m × 2 の 形式の非ゼロ値の有限集合では、s が 1 または −1 で、m および e は特定の浮動小数点型によって決まります。float の場合、0 <m< 2²⁴ and −149 ≤ e ≤ 104 であり、double の場合、0 <m< 2⁵³ and −1075 ≤ e ≤ 970 です。 非正規化浮動小数点数は有効な非ゼロ値とみなされます。 C# では、準拠実装が非正規化浮動小数点数をサポートすることを要求も禁止もしていません。

float型は、7 桁の精度で約 1.5 × 10⁻⁴⁵ から 3.4 × 10³⁸ までの範囲の値を表すことができます。

double型は、15 から 17 桁の精度で約 5.0 × 10⁻³²⁴ から 1.7 × 10³⁰⁸ までの範囲の値を表すことができます。

二項演算子のいずれかのオペランドが浮動小数点型の場合、§12.4.7 に詳述されているように標準の数値昇格が適用され、float または double精度で演算が実行されます。

代入演算子を含む浮動小数点演算子は、例外を生成することはありません。 代わりに、例外的な状況では、浮動小数点演算によって、以下に示すようにゼロ、無限大、または NaN が生成されます。

  • 浮動小数点演算の結果は、変換先の形式で表現可能な最も近い値で四捨五入されます。
  • 浮動小数点演算の結果の大きさが変換先の形式に対して小さすぎる場合、演算の結果は正のゼロまたは負のゼロになります。
  • 浮動小数点演算の結果の大きさが変換先の形式に対して大きすぎる場合、演算の結果は正の無限大または負の無限大になります。
  • 浮動小数点演算が無効な場合、演算の結果は NaN になります。
  • 浮動小数点演算のオペランドの一方または両方が NaN の場合、演算の結果は NaN になります。

浮動小数点演算は、演算の結果の型よりも高い精度で実行される場合があります。 浮動小数点型の値をその型の正確な精度に強制するには、明示的なキャスト (§12.9.7) を使用できます。

: 一部のハードウェア アーキテクチャでは、double 型よりも範囲と精度が広い「extended」または「long double」浮動小数点型をサポートしており、すべての浮動小数点演算をこのより高い精度の型を使用して暗黙的に実行します。 このようなハードウェア アーキテクチャでは、パフォーマンスを大幅に犠牲にすることでのみ、精度の低い浮動小数点演算を実行できます。C# では、パフォーマンスと精度の両方を犠牲にする実装を要求するのではなく、すべての浮動小数点演算に高精度の型を使用できます。 より正確な結果をもたらす以外には、測定可能な効果はほとんどありません。 ただし、乗算によって x * y / z> の範囲外の結果が生成され、その後の除算によって一時的な結果が double の範囲に戻される double 形式の式では、式がより広い範囲の形式で評価されるため、無限大ではなく有限の結果が生成されることがあります。 終了サンプル

8.3.8 Decimal 型

decimal 型は 128 ビットのデータ型で、財務や通貨の計算に適しています。 decimal 型は、少なくとも -7.9 × 10⁻²⁸ から 7.9 × 10²⁸ までの範囲の値を、少なくとも 28 桁の精度で表現できます。

decimal の値の有限集合は (–1)ᵛ × c × 10⁻ᵉ の形式で、符号 v は、0 または 1、係数 c は 0 ≤ c<Cmax で指定され、スケール e は、EmineEmax となり、ここでは、Cmax は少なくとも 1 × 10²⁸、Emin ≤ 0 および Emax ≥ 28 となります。 decimal 型は必ずしも符号付きゼロ、無限大、または NaN をサポートするわけではありません。

decimal は 10 の累乗でスケールされた整数として表されます。 絶対値が decimal 未満の 1.0m の場合、値は少なくとも小数点第 28 位まで正確です。 絶対値が decimal 以上である 1.0m の場合、値は少なくとも小数点第 28 位まで正確です。 float および double データ型とは異なり、0.1 などの小数は、小数表現で正確に表現できます。 float および double 表現では、このような数値は多くの場合、終了しないバイナリ展開を持ち、それらの表現では切り捨てエラーが発生しやすくなります。

二項演算子のいずれかのオペランドが decimal 型の場合、§12.4.7 に詳述されているように標準の数値昇格が適用され、演算は、double 精度で実行されます。

decimal 型の値に対する演算の結果は、正確な結果を計算し (各演算子の定義に従ってスケールを保持)、表現で四捨五入します。 結果は最も近い表現可能な値で四捨五入され、結果が 2 つの表現可能な値に等しく近い場合は、最下位桁の位置に偶数を持つ値で四捨五入されます (これは「銀行型丸め」と呼ばれます)。 つまり、結果は少なくとも小数点第 28 位まで正確です。 丸めによって、ゼロ以外の値からゼロ値が生成される場合があることに注意してください。

decimal 算術演算によって生成された結果の大きさが decimal 形式には大きすぎる場合、System.OverflowException がスローされます。

decimal 型は浮動小数点型よりも精度は高くなりますが、範囲は狭くなる可能性があります。 したがって、浮動小数点型から decimal への変換ではオーバーフロー例外が発生する可能性があり、decimal から浮動小数点型への変換では精度の低下やオーバーフロー例外が発生する可能性があります。 これらの理由により、浮動小数点型と decimal の間には暗黙的な変換は存在せず、明示的なキャストがない場合、浮動小数点オペランドと decimal オペランドが同じ式内に直接混在するとコンパイル時エラーが発生します。

8.3.9 ブール型

bool 型はブール論理量を表します。 型 bool に指定できる値は、true および false です。 falseの表現は §8.3.3で説明されています。 true の表現方法は特に指定しませんが、false の表現方法とは異なります。

bool と他の値型の間には標準的な変換は存在しません。 特に、bool 型は整数型とは区別され分離されているため、整数値の代わりに bool 値を使用することはできず、その逆も同様です。

: C 言語および C++ 言語では、ゼロの整数値または浮動小数点値、または NULL ポインタをブール値 false に変換でき、ゼロ以外の整数値または浮動小数点値、または NULL 以外のポインタをブール値 true に変換できます。 C# では、このような変換は、整数値または浮動小数点値を 0 と明示的に比較するか、オブジェクト参照を null と明示的に比較することによって実行されます。 注釈

8.3.10 列挙型

列挙型は、名前付き定数を持つ固有の型です。 すべての列挙型には基礎となる型があり、それは、bytesbyteshortushortintuintlong または ulong です。 列挙型の値のセットは、その基になる型の値のセットと同じです。 列挙型の値は、名前付き定数の値に制限されません。 列挙型は列挙宣言 (§19.2) をで定義されます。

8.3.11 タプル型

タプル型は、オプションの名前と個別の型を持つ、順序付けられた固定長の値のシーケンスを表します。 タプル型内の要素の数は、アリティと呼ばれます。 タプル型は、n≥2 である (T1 I1, ..., Tn In) で記述され、ここでは、識別子 I1...In はオプションのタプル要素名です。

この構文は、型 T1...Tn から System.ValueTuple<...> で構築された型の省略形であり、2 から 7 までの任意のアリティのタプル型を直接表現できる汎用構造体型のセットになります。 対応する数の型パラメータを持つタプル型のアリティに直接一致する System.ValueTuple<...> 宣言が存在する必要はありません。 代わりに、7 を超えるアリティを持つタプルは、タプル要素に加えて、別の System.ValueTuple<T1, ..., T7, TRest> 型を使用して残りの要素のネストされた値を含む Rest フィールドを持つジェネリック構造体型 System.ValueTuple<...> で表されます。 このようなネスティングは、たとえば、Rest フィールドの存在など、さまざまな方法で見られます。 1 つの追加フィールドのみが必要な場合、ジェネリック構造体型 System.ValueTuple<T1> が使用されます。この型自体はタプル型とは見なされません。 7 つ以上の追加フィールドが必要な場合は、System.ValueTuple<T1, ..., T7, TRest> が再帰的に使用されます。

タプル型内の要素名は、異なる名前である必要があります。 ItemX 形式のタプル要素名 (X はタプル要素の位置を表す、0 で始まらない任意の 10 進数字のシーケンス) は、X で示される位置でのみ許可されます。

オプションの要素名は ValueTuple<...> 型では表現されず、タプル値の実行時間表現には格納されません。 変換不要な要素型並びがあるタプル間には同一性変換 (§10.2.2) が存在します。

new 演算子 §12.8.17.2 は、タプル型構文 new (T1, ..., Tn) には適用できません。 タプル値はタプル式 (§12.8.6) から作成するか、new から構築された型に ValueTuple<...> 演算子を直接適用することで作成できます。

タプル要素は、Item1Item2 などの名前を持つパブリック フィールドであり、タプル値のメンバー アクセス経由でアクセスできます (§12.8.7)。 さらに、タプル型に特定の要素の名前がある場合は、その名前を使用して問題の要素にアクセスできます。

: 大きなタプルがネストされた System.ValueTuple<...> 値で表されている場合でも、各タプル要素には、その位置に対応する Item... 名を使用して直接アクセスできます。 注釈

: 次の例について考えます。

(int, string) pair1 = (1, "One");
(int, string word) pair2 = (2, "Two");
(int number, string word) pair3 = (3, "Three");
(int Item1, string Item2) pair4 = (4, "Four");
// Error: "Item" names do not match their position
(int Item2, string Item123) pair5 = (5, "Five");
(int, string) pair6 = new ValueTuple<int, string>(6, "Six");
ValueTuple<int, string> pair7 = (7, "Seven");
Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}");

pair1pair2 および pair3 のタプル型はすべて有効で、タプル型要素の一部またはすべてに名前が付いています。

pair4 のタプル型は、名前 Item1Item2 の位置が一致するため有効ですが、名前 pair5 および Item2 の位置は一致しないため、Item123 タプル型は許可されません。

pair6pair7 の宣言は、タプル型が形式 ValueTuple<...> の構築型と交換可能であり、後者の構文では new 演算子が許可されていることを示しています。

最後の行は、タプル要素がその位置に対応する Item 名でアクセスできるだけでなく、型内に存在する場合は対応するタプル要素名でもアクセスできることを示しています。 終了サンプル

8.3.12 null 許容値型

null 許容値照型は、基底となる型のすべての値と追加の null 値を表します。 null 許容値型は、T? と記述され、ここでは T は、基底型です。 この構文は System.Nullable<T> の省略形であり、2 つの形式は互換的に使用できます。

逆に、null 非許容値型は、System.Nullable<T> 以外の任意の値型とその省略形 T? (任意の T の場合) で、null 非許容値型として制約される任意の型パラメータ (つまり、値型制約 (§15.2.5) を持つ任意の型パラメータ) です。 System.Nullable<T> 型は T の値型の制約を指定します。つまり、null 許容値型の基になる型は、null 非許容値型にすることができます。 null 許容値型の基になる型は、null 許容値型または参照型にはできません。 たとえば、int?? は、無効な型です。 null 許容参照型については、§8.9 を参照してください。

null 許容値型 T? のインスタンスには、2 つのパブリック読み取り専用プロパティがあります。

  • HasValuebool プロパティ
  • ValueT プロパティ

HasValuetrue のインスタンスは、null ではないと認識されています。 null ではなインスタンスには、既知の値が含まれ、Value は、その値を返します。

HasValuefalse のインスタンスは、null と認識されています。 null インスタンスには、未定義の値があります。 null インスタンスの Value を読み込もうとすると System.InvalidOperationException がスローされます。 null 許容インスタンスの Value プロパティにアクセスするプロセスは、アンラップと呼ばれています。

デフォルトのコンストラクタに加え、各 null 許容値型 T? には、型 T の単一パラメータがあるパブリック コンストラクタがあります。 型 x の 値 T が指定された場合、形式のコンストラクタ呼び出しにより

new T?(x)

T? プロパティが Value である x の null 以外のインスタンスが作成されます。 特定の値に対して null 許容値型の null 以外のインスタンスを作成するプロセスは、ラッピングと呼ばれています。

暗黙的な変換は null リテラルから T? (§10.2.7) および T から T? (§10.2.6) で利用できます。

null 許容値型 T? はインターフェイスを実装しません (§18)。 特に、これは、基礎となる型 T が実装するインターフェースを実装していないことを意味します。

8.3.13 ボックス化とボックス化解除

ボックス化とアンボックス化の概念は、型 間で変換される value_type の任意の値を許可することで value_type および object 間を橋渡しします。 ボックス化とアンボックス化により、型システムの統一されたビューが可能になり、任意の型の値を最終的に object として扱うことができます。

ボックス化については §10.2.9 を、アンボックス化については §10.3.7 を参照してください。

8.4 構築型

8.4.1 全般

ジェネリック型宣言は、それ自体が未束縛ジェネリック型を表し、これは型引数を適用することで、さまざまな型を形成するための「ブループリント」として使用されます。 型引数は、ジェネリック型の名前の直後に山括弧 (< および >) で囲んで記述されます。 少なくとも 1 つの型引数を含む型は、構築型と呼ばれます。 構築型は、型名が出現できる言語のほとんどの場所で使用できます。 未束縛ジェネリック型は typeof_expression (§12.8.18) 内でのみ使用できます。

構築型は、式の中で単純な名前として (§12.8.4) 使用でき、またメンバーにアクセスするときにも使用できます (§12.8.7)。

namespace_or_type_name が評価される際は、正しい数の型パラメータを持つジェネリック型のみが考慮されます。 したがって、型パラメータの数が異なる限り、同じ識別子を使用して異なる型を識別することができます。 これは、同じプログラム内でジェネリック クラスと非ジェネリック クラスを混在させる場合に便利です。

:

namespace Widgets
{
    class Queue {...}
    class Queue<TElement> {...}
}

namespace MyApplication
{
    using Widgets;

    class X
    {
        Queue q1;      // Non-generic Widgets.Queue
        Queue<int> q2; // Generic Widgets.Queue
    }
}

終了サンプル

namespace_or_type_name 生成物における名前検索の詳細な規則につては、§7.8 を参照してください。 これらの生成物の曖昧さの解決については、§6.2.5 を参照してください。 type_name は、型パラメータを直接指定していなくても、構築型を識別する場合があります。 これは、型がジェネリック class 宣言内にネストされ、それを含む宣言のインスタンスの型が名前の検索に暗黙的に使用される場合に発生する可能性があります (§15.3.9.7)。

:

class Outer<T>
{
    public class Inner {...}

    public Inner i; // Type of i is Outer<T>.Inner
}

終了サンプル

非列挙構築型は unmanaged_type として使用できません (§8.8)。

8.4.2 型引数

型引数リストの各引数は、純粋にです。

type_argument_list
    : '<' type_argument (',' type_argument)* '>'
    ;

type_argument
    : type
    | type_parameter nullable_type_annotation?
    ;

各型引数は対応する型パラメータに対する制約を満たす必要があります (§15.2.5)。 参照型引数の null 可能性が型パラメータの null 可能性と一致しない場合、制約は満たされますが、警告が表示される場合があります。

8.4.3 オープン型およびクローズド型

すべてのタイプは、オープン型またはクローズド型に分類できます。 オープン型は、型パラメータを含む型です。? 具体的には次のとおりです。

  • 型パラメータは、オープン型を定義します。
  • 配列型は、その要素型がオープン型の場合のみ、オープン型になります。
  • 構築型は、その型引数の 1 つ以上がオープン型である場合にのみ、オープン型になります。 構築ネスト型は、その型引数の 1 つ以上、またはそれに含まれる型の 1 つ以上の型引数がオープン型である場合にのみ、オープン型になります。

クローズド型はオープン型ではない型です。

実行時に、ジェネリック型宣言内のすべてのコードは、ジェネリック宣言に型引数を適用することによって作成されたクローズ構築型のコンテキストで実行されます。 ジェネリック型内の各型パラメータは、特定のランタイム型にバインドされます。 すべてのステートメントと式の実行時処理は常にクローズド型で行われ、オープン型はコンパイル時処理中にのみ行われます。

同じ未束縛ジェネリック型から構築され、対応する型引数のそれぞれの間に同一性変換が存在する場合、2 つのクローズド構築型は、同一性変換可能 (§10.2.2) です。 対応する型引数自体は、クローズド構築型、または同一性変換可能なタプルである場合があります。 同一性変換可能なクローズド構築型は、単一の静的変数セットを共有します。 それ以外の場合、それぞれのクローズド構築型には独自の静的変数のセットが存在します。 オープン型は実行時には存在しないため、オープン型に関連付けられた静的変数は存在しません。

8.4.4 束縛型および未束縛型

未束縛型という用語は、非ジェネリック型または未束縛ジェネリック型を指します。 束縛型という用語は、非ジェネリック型または構築型を指します。

未束縛型は、型宣言によって宣言されたエンティティを指します。 未束縛ジェネリック型自体は、型ではなく、変数の型、引数または戻り値または基底型として使用できません。 未束縛ジェネリック型を参照できる唯一の構造は、typeof 式です (§12.8.18)。

8.4.5 制約を満たす

構築型またはジェネリック メソッドが参照されるたびに、指定の型引数はジェネリック型またはメソッドで宣言された型パラメータ制約と照合します (§15.2.5)。 各 where 句の場合、名前付き型パラメータに対応する型引数 A が次のように各制約に対してチェックされます。

  • 制約が class 型、インターフェイス型、または型パラメータである場合、制約内に現れる型パラメータの代わりに指定された型引数を使用して、その制約を C で表します。 制約を満たすには、型 A が次のいずれかによって型 C に変換可能でなければなりません。
    • 同一性変換 (§10.2.2)
    • 暗黙的参照変換 (§10.2.8)
    • ボクシング変換 (§10.2.9)、ただし、型 A は、null 非許容値型です。
    • 型パラメータ A から C への暗黙的参照、ボックス化、または型パラメータ変換。
  • 制約が参照型制約 (class) の場合、型 A は次のいずれかを満たす必要があります。
    • A は、インターフェイス型、クラス型、デリゲート型、配列型、または動的型です。

    : System.ValueType および System.Enum はこの制約を満たす参照型です。 注釈

    • A は、参照型として知られている型パラメータです (§8.2)。
  • 制約が値型制約 (struct) の場合、型 A は次のいずれかを満たす必要があります。
    • A は、struct 型または enum ですが、null 許容値型ではありません。

    : System.ValueType および System.Enum はこの制約を満たさない参照型です。 注釈

    • A は、値型制約がある型パラメータです (§15.2.5)。
  • 制約が、コンストラクタ制約 new() の場合、型 A は、abstract にならず、一般的なパラメータなしのコンストラクタになります。 これは、次のいずれかが True の場合に満たされます。
    • A は、すべての値型にパブリックのデフォルト コンストラクタがあるため、値型です (§8.3.3)。
    • A は、コンストラクタ制約がある型パラメータです (§15.2.5)。
    • A は、値型制約がある型パラメータです (§15.2.5)。
    • A は、は抽象的ではなく、パラメータなしで明示的に宣言されたパブリック コンストラクタを含む class です。
    • A は、 abstract ではなく、デフォルト コンストラクタがあります (§15.11.5)。

指定された型引数によって型パラメータの制約の 1 つ以上が満たされない場合、コンパイル時エラーが発生します。

型パラメータは継承されないため、制約も継承されません。

: 以下では、Dは型パラメータ T に制約を指定して、TclassB<T> 基底クラスによって課された制約を満たすようにする必要があります。 対照的に、class は任意の E に対して List<T> を実装するため、IEnumerableT は制約を指定する必要はありません。

class B<T> where T: IEnumerable {...}
class D<T> : B<T> where T: IEnumerable {...}
class E<T> : B<List<T>> {...}

終了サンプル

8.5 型パラメーター

型パラメータは、実行時にパラメータがバインドされる値型または参照型を指定する識別子です。

type_parameter
    : identifier
    ;

型パラメータはさまざまな型引数を使用してインスタンス化できるため、型パラメータには他の型と若干異なる操作と制限があります。

: これには次が含まれます。

  • 型パラメータは、基底クラス (§15.2.4.2) またはインターフェイス (§18.2.4) を宣言するために直接使用することはできません。
  • 型パラメータのメンバー検索のルールは、型パラメータに適用された制約 (存在する場合) によって異なります。 詳細については、§12.5 を参照してください。
  • 型パラメータに使用できる変換は、型パラメータに適用された制約 (存在する場合) によって異なります。 詳細については、§10.2.12 および §10.3.8 を参照してください。
  • 型パラメータが参照型であることが分かっている場合を除き、リテラル null は型パラメータによって指定された型に変換できません (§10.2.12)。 ただし、代わりにデフォルト式 (§12.8.21) を使用することもできます。 さらに、型パラメータによって指定された型の値は、型パラメータに値型制約がない限り、 および == (!=) を使用して null と比較できます
  • new 式 (§12.8.17.2) は、型パラメータが constructor_constraint または値型制約 (§15.2.5) によって制約されている場合にのみ、型パラメータで使用できます。
  • 型パラメーターは属性内のどこにも使用できません。
  • 型パラメータは、静的メンバーまたはネストされた型を識別するためにメンバー アクセス (§12.8.7) または型名 (§7.8) で使用することはできません。
  • 型パラメータは unmanaged_type として使用できません (§8.8)。

注釈

型として、型パラメータは純粋にコンパイル時の構成要素です。 実行時に、各型パラメータは、ジェネリック型宣言に型引数を提供することによって指定された実行時型にバインドされます。 したがって、型パラメータで宣言された変数の型は、実行時に閉じた構築型になります §8.4.3。 型パラメータを含むすべてのステートメントと式の実行時実行では、そのパラメータの型引数として指定された型が使用されます。

8.6 式ツリー型

式ツリー を使用すると、ラムダ式を実行可能コードではなくデータ構造として表現できます。 式ツリーは、形式 の値で、ここでは、System.Linq.Expressions.Expression<TDelegate> は任意のデリゲート型です。 この仕様の残りの部分では、これらの型は省略形 Expression<TDelegate> を使用して参照されます。

ラムダ式からデリゲート型 D への変換が存在する場合、式ツリー型 Expression<TDelegate> への変換も存在します。 ラムダ式をデリゲート型に変換すると、ラムダ式の実行可能コードを参照するデリゲートが生成されますが、式ツリー型に変換すると、ラムダ式の式ツリー表現が作成されます。 この変換に関する詳細については、§10.7.3 を参照してください。

: 次のプログラムは、ラムダ式を実行可能コードと式ツリーの両方として表します。 Func<int,int> への変換が存在するため、Expression<Func<int,int>> への変換も存在します。

Func<int,int> del = x => x + 1;             // Code
Expression<Func<int,int>> exp = x => x + 1; // Data

これらの割り当てに続いて、デリゲート delx + 1 を返すメソッドを参照し、式ツリー exp は式 x => x + 1 を記述するデータ構造を参照します。

終了サンプル

Expression<TDelegate> は、インスタンス メソッド Compile を指定します。これはデリゲート型 TDelegate を生成します。

Func<int,int> del2 = exp.Compile();

このデリゲートを呼び出すことで、式ツリーで表されるコードが実行されます。 したがって、上記の定義によれば、deldel2 は同等であり、次の 2 つのステートメントは同じ効果を持ちます。

int i1 = del(1);
int i2 = del2(1);

このコードを実行後、i1i2 は両方とも、値 2 を持ちます。

Expression<TDelegate> が指定する API サーフェスは、上記の Compile メソッドの要件を超えた処理系定義です。

: 式ツリーに提供される API の詳細は処理系定義ですが、実装は次のように想定されます。

  • ラムダ式からの変換の結果として作成された式ツリーの構造をコードが検査して応答できるようにします。
  • ユーザーコード内でプログラム的に式ツリーを作成できるようにする

注釈

8.7 動的型

dynamic は、他のすべての型で使用される静的バインディングとは対照的に、§12.3.2 で詳細に説明されている動的バインディングを使用します。

dynamic は、次の点を除いて object と同一であると見なされます。

  • dynamic の式に対する演算は動的にバインドできます (§12.3.3)。
  • 両方が候補である場合、型推論 (§12.6.3) は、dynamic より object を優先します。
  • dynamic は、以下として使用できません。
    • object_creation_expression の型 (§12.8.17.2)
    • class_base (§15.2.4)
    • member_accesspredefined_type (§12.8.7.1)
    • typeof 演算子のオペランド
    • 属性引数
    • 制約
    • 拡張メソッド型
    • struct_interfaces (§16.2.5) または interface_type_list 内の型引数の一部 (§15.2.4.1)。

この同等性のため、次のことが成り立ちます。

  • 暗黙的 ID 変換がある
    • object から dynamic の間
    • dynamicobject に置き換える際と同じ構築型間
    • dynamicobject に置き換える際と同じタプル型間
  • object との間の暗黙的および明示的な変換は、dynamic との間でも適用されます。
  • dynamicobject に置き換えたときに同じ署名は、同じ署名とみなされます。
  • 実行時には型 dynamic と型 object は区別できません。
  • dynamic の式は動的式と呼ばれます。

8.8 アンマネージ型

unmanaged_type
    : value_type
    | pointer_type     // unsafe code support
    ;

unmanaged_type は、reference_type でも type_parameter でもなく、アンマネージドに制約されておらず、型が unmanaged_typeではないインスタンス フィールドを含まない型です。 つまり、unmanaged_type は次のいずれかになります。

  • sbytebyteshortushortintuintlongulongcharfloatdoubledecimal または bool
  • すべての enum_type
  • unmanaged_type のインスタンス フィールドのみを含む、ユーザー定義の struct_type
  • アンマネージドであることを制約とする任意の型パラメーター。
  • 任意の pointer_type (§23.3)。

8.9 参照型および null 可能性

8.9.1 全般

null 許容参照型は、nullable_type_annotation (?) を null 非許容参照型に追加することであらわされます。 null 非許容参照型とそれに対応する null 値許容型の間には意味的な違いはなく、どちらもオブジェクトまたは null への参照になります。 nullable_type_annotation の有無は、式が null 値を許可するかどうかを宣言します。 式がその意図に従って使用されていない場合、コンパイラは診断を提供することがあります。 式の null 状態については、§8.9.5 で定義されています。 null 許容参照型とそれに対応する null 非許容参照型の間には同一性変換が存在します (§10.2.2)。

参照型には、2 つの形式の null 可能性があります。

  • nullable: nullable-reference-typenull に割り当てることができます。 デフォルトの null 状態は、maybe-null です。
  • non-nullable: non-nullable referencenull 値に割り当てることはできません。 デフォルトの null 状態は、not-null です。

注:R および R? は、同じ基になる型 R で表されます。 その基になる型の変数には、オブジェクトへの参照が含まれるか、または「参照なし」を示す値 null になります。 注釈

null 許容参照型とそれに対応する null 非許容参照型の構文上の区別により、コンパイラは診断を生成できます。 コンパイラは、§8.2.1 で定義されている nullable_type_annotation を許可する必要があります。 診断は警告に限定する必要があります。 コンパイル時に生成される診断メッセージの変更を除き、null 許容注釈の有無や null 許容コンテキストの状態によって、プログラムのコンパイル時または実行時の動作が変更することはできません。

8.9.2 null 非許容参照型

null 非許容参照型 は、T 形式の参照型で、ここでは、T は、型名です。 null 非変数のデフォルトの null 状態は、not-null です。 null でない値が必要な場所でmaybe-null である式が使用されると警告が表示される場合があります。

8.9.3 null 許容参照型

T? などの 形式 string? の参照型は、null 許容参照型です。 null 許容変数のデフォルトの null 状態は、maybe null です。 注釈 ? は、この型が null 許容であることを示します。 コンパイラはこれらの意図を認識して警告を発行できます。 null 許容注釈コンテキストが無効になっている場合、この注釈を使用すると警告が生成されることがあります。

8.9.4 null 許容コンテキスト

8.9.4.1 全般

ソース コードの各行には、null 許容コンテキストがあります。 null 許容コンテキストの注釈と警告フラグは、それぞれ null 許容注釈 (§8.9.4.3) と null 許容警告 (§8.9.4.4) を制御します。 各フラグは、有効または無効です。 コンパイラは、静的フロー分析を使用して、任意の参照変数の null 状態を判断できます。 参照変数の null 状態 (§8.9.5)は、not nullmaybe nullmaybe default のいずれかとなります。

null 許容コンテキストは、null 許容ディレクティブ (§6.5.9) を介してソース コード内で指定することも、ソース コードの外部にある実装固有のメカニズムを介して指定することもできます。 両方のアプローチを使用する場合、null 許容ディレクティブは外部メカニズムを介して行われた設定を優先します。

null 許容コンテキストのデフォルトの状態は処理系定義です。

この仕様全体を通じて、null 許容ディレクティブが含まれていない、または現在の null 許容コンテキストの状態に関して何も記述されていないすべての C# コードは、注釈と警告の両方が有効になっている null 許容コンテキストを使用してコンパイルされているものと想定されます。

注: 両方のフラグが無効になっている null 許容コンテキストは、参照型の以前の標準動作と一致します。 注釈

8.9.4.2 無効な null 許容

警告フラグと注釈フラグの両方が無効になっている場合、null 許容コンテキストは無効になります。

null 許容コンテキストが無効の場合:

  • 注釈のない参照型の変数が null で初期化されるか、またはその値が割り当てられる場合、警告は生成されません。
  • 参照型の変数が null 値を持つ可能性がある場合、警告は生成されません。
  • 参照型 T の場合、? の注釈 T? は、メッセージを生成し、型 T? は、T と同じになります。
  • 任意の型パラメータ制約 where T : C? の場合、? の注釈 C? は、メッセージを生成し、C? は、C と同じになります。
  • 任意の型パラメータ制約 where T : U? の場合、? の注釈 U? は、メッセージを生成し、U? は、U と同じになります。
  • ジェネリック制約 class? は、警告メッセージを生成します。 型パラメータは、参照型にする必要があります。

    : このメッセージは、関連のない null 許容警告設定の状態との混同を避けるため、「警告」ではなく「情報」として特徴付けられています。 注釈

  • null 許容演算子 ! (§12.8.9) には効果はありません。

:

#nullable disable annotations
string? s1 = null;    // Informational message; ? is ignored
string s2 = null;     // OK; null initialization of a reference
s2 = null;            // OK; null assignment to a reference
char c1 = s2[1];      // OK; no warning on dereference of a possible null;
                      //     throws NullReferenceException
c1 = s2![1];          // OK; ! is ignored

終了サンプル

8.9.4.3 null 許容注釈

警告フラグが無効で、注釈フラグが有効になっている場合、null 許容コンテキストは、注釈です。

null 許容コンテキストが注釈の場合:

  • 任意の参照型 T の場合、? の注釈 T? は、T? が null 許容型であることを示しますが、注釈が無い T は、null 非許容型です。
  • null 可能性に関連する診断警告は生成されません。
  • null 許容演算子 ! (§12.8.9) は、そのオペランドの分析された null 状態と、生成されるコンパイル時の診断警告を変更する場合があります。

:

#nullable disable warnings
#nullable enable annotations
string? s1 = null;    // OK; ? makes s2 nullable
string s2 = null;     // OK; warnings are disabled
s2 = null;            // OK; warnings are disabled
char c1 = s2[1];      // OK; warnings are disabled; throws NullReferenceException
c1 = s2![1];          // No warnings

終了サンプル

8.9.4.4 null 許容の警告

警告フラグが有効で、注釈フラグが無効になっている場合、null 許容コンテキストは、警告です。

null 許容コンテキストが、警告の場合、コンパイラは、次のケースで診断を生成します。

  • maybe null であると判断された参照変数が逆参照されます。
  • null 非許容型の参照変数は、maybe null の式に割り当てられます。
  • ? は、null 許容参照型であることを示すために使用されます。
  • null 許容演算子 ! (§12.8.9) を使用すると、そのオペランドの null 状態を not null に設定できます。

:

#nullable disable annotations
#nullable enable warnings
string? s1 = null;    // OK; ? makes s2 nullable
string s2 = null;     // OK; null-state of s2 is "maybe null"
s2 = null;            // OK; null-state of s2 is "maybe null"
char c1 = s2[1];      // Warning; dereference of a possible null;
                      //          throws NullReferenceException
c1 = s2![1];          // The warning is suppressed

終了サンプル

8.9.4.5 有効な null 許容

警告フラグと注釈フラグの両方が有効な場合、null 許容コンテキストは有効になります。

null 許容コンテキストが有効の場合:

  • 任意の参照型 T の場合、? の注釈 T? は、T? が null 許容型にしますが、注釈が無い T は、null 非許容型です。
  • コンパイラは、静的フロー分析を使用して、任意の参照変数の null 状態を判断できます。 null 許容警告が有効である場合、参照変数の null 状態 (§8.9.5) は、not nullmaybe nullmaybe default のいずれかになります。
  • null 許容演算子 ! (§12.8.9) は、そのオペランドの null 状態を not null に設定します。
  • 型パラメーターの null 許容が対応する型引数の null 許容値と一致しない場合、コンパイラは警告を発行できます。

8.9.5 null 可能性と null 状態

8.9.5.1 全般

コンパイラは静的分析を実行する必要はなく、null 可能性に関連する診断警告を生成する必要もありません。

このサブ句の残りの部分は条件付き規範です。

8.9.5.2 フロー分析

診断警告を生成するコンパイラはこれらの規則に準拠します。

すべての式には、次の 3 つの null 状態があります。

  • maybe null: 式の値は null と評価される場合があります。
  • maybe default: 式の値は、その型のデフォルト値に評価される場合があります。
  • not null: 式の値は null ではありません。

式のデフォルトの null 状態は、その型と、宣言時の注釈フラグの状態によって決まります。

  • null 許容参照型のデフォルト null 状態は、
    • 注釈フラグが有効になっているテキスト内で宣言されている場合は、Maybe null です。
    • 注釈フラグが無効になっているテキスト内で宣言されている場合は、Not null です。
  • null 非許容参照型のデフォルト null 状態は、not null です。

注:maybe default 状態は、型が string などの null 非許容型であり、式 default(T) が null 値である場合に、制約のない型パラメータで使用されます。 null は null 非許容型のドメイン内にないため、状態はおそらくデフォルトです。 注釈

注釈フラグが有効になっているテキストでその変数が宣言されているときに、null 非許容参照型の変数 (§9.2.1) が初期化されるか、maybe null の式に割り当てられると、診断が生成されます。

: パラメータが null 許容であり、その値が null 非許容型に割り当てられている次のメソッドを検討します。

#nullable enable
public class C
{
    public void M(string? p)
    {
        // Warning: Assignment of maybe null value to non-nullable variable
        string s = p;
    }
}

コンパイラは、null である可能性があるパラメーターが null ではない変数に割り当てられる警告を発行することがあります。 割り当て前にパラメーターが null チェックされている場合、コンパイラは null 許容状態分析でパラメーターを使用し、警告を発行しない可能性があります。

#nullable enable
public class C
{
    public void M(string? p)
    {
        if (p != null)
        {
            string s = p; // No warning
            // Use s
        }
    }
}

終了サンプル

コンパイラは、分析の一環として変数の null 状態を更新できます。

: コンパイラは、プログラム内のステートメントに基づいて状態を更新することを選択できます。

#nullable enable
public void M(string? p)
{
    int length = p.Length; // Warning: p is maybe null

    string s = p; // No warning. p is not null

    if (s != null)
    {
        int l2 = s.Length; // No warning. s is not null
    }
    int l3 = s.Length; // Warning. s is maybe null
}

前の例では、ステートメントの int length = p.Length;後に、p の null 状態が null でないとコンパイラが判断する場合があります。 null だった場合、そのステートメントは NullReferenceException をスローします。 これは、コードの前に if (p == null) throw NullReferenceException(); があった場合の動作と似ていますが、記述されたコードによって警告が生成される可能性がある点が異なります。その目的は、例外が暗黙的にスローされる可能性があることを警告することです。 終了サンプル

メソッドの後半では、コードは s が null 参照ではないことを確認します。 s の null 状態は、null チェック ブロックがクローズした後に maybe null に変更できます。 コンパイラは、s が null であった可能性があることを前提としてコードが記述されているため、null である可能性があることを推測できます。 一般に、コードに null チェックが含まれている場合、コンパイラは値が null であった可能性があることを推測できます。

: 次の各式には、何らかの形式の null チェックが含まれています。 o の null 状態は、次の各ステートメントの後に null 以外から null に変更される可能性があります。

#nullable enable
public void M(string s)
{
    int length = s.Length; // No warning. s is not null

    _ = s == null; // Null check by testing equality. The null state of s is maybe null
    length = s.Length; // Warning, and changes the null state of s to not null

    _ = s?.Length; // The ?. is a null check and changes the null state of s to maybe null
    if (s.Length > 4) // Warning. Changes null state of s to not null
    {
        _ = s?[4]; // ?[] is a null check and changes the null state of s to maybe null
        _ = s.Length; // Warning. s is maybe null
    }
}

自動プロパティとフィールドに似たイベント宣言の両方で、コンパイラによって生成されたバッキング フィールドが使用されます。 Null 状態分析では、イベントまたはプロパティへの割り当てがコンパイラによって生成されたバッキング フィールドへの割り当てであると推測される場合があります。

: コンパイラは、自動プロパティまたはフィールドに似たイベントを記述すると、対応するコンパイラによって生成されたバッキング フィールドを書き込むかどうかを判断できます。 プロパティの null 状態は、バッキング フィールドの状態と一致します。

class Test
{
    public string P
    {
        get;
        set;
    }

    public Test() {} // Warning. "P" not set to a non-null value.

    static void Main()
    {
        var t = new Test();
        int len = t.P.Length; // No warning. Null state is not null.
    }
}

前の例では、コンストラクターは P を null 以外の値に設定せず、コンパイラが警告を発行する可能性があります。 プロパティの型が null 非許容参照型であるため、P プロパティにアクセスしても警告はありません。 終了サンプル

コンパイラは、プロパティ (§15.7) を状態を持つ変数として、または独立した get アクセサーおよび set アクセサー (§15.7.3) として扱うことができます。

: コンパイラは、プロパティへの書き込みがプロパティの読み取りの null 状態を変更するか、プロパティを読み取る場合にそのプロパティの null 状態を変更するかを選択できます。

class Test
{
    private string? _field;
    public string? DisappearingProperty
    {
        get
        {
            string tmp = _field;
            _field = null;
            return tmp;
        }
        set
        {
            _field = value;
        }
    }

    static void Main()
    {
        var t = new Test();
        if (t.DisappearingProperty != null)
        {
            int len = t.DisappearingProperty.Length; // No warning. A compiler can assume property is stateful
        }
    }
}

前述の例では、DisappearingProperty のバッキング フィールドは、読み取り時に null に設定されます。 ただし、コンパイラは、プロパティを読み取ってもその式の null 状態は変更されないと想定する場合があります。 終了サンプル

コンパイラは、変数、プロパティ、またはイベントを逆参照する任意の式を使用して、null 状態を null 以外に設定できます。 null の場合、逆参照式は NullReferenceException をスローします。

:


public class C
{
    private C? child;

    public void M()
    {
        _ = child.child.child; // Warning. Dereference possible null value
        var greatGrandChild = child.child.child; // No warning. 
    }
}

終了サンプル

8.9.5.3 型変換

診断警告を生成するコンパイラはこれらの規則に準拠します。

手記: 型の最上位レベルまたは入れ子になった null 許容注釈の違いは、null 非許容参照型とそれに対応する null 許容型 (§8.9.1) の間にセマンティックな違いがないため、型間の変換が許可されるかどうかには影響しません。 注釈

コンパイラは、変換が縮小されるときに、最上位レベルまたは入れ子になった 2 つの型の間で null 許容注釈が異なる場合に警告を発行することがあります。

: 最上位レベルの注釈が異なる型

#nullable enable
public class C
{
    public void M1(string p)
    {
        _ = (string?)p; // No warning, widening
    }

    public void M2(string? p)
    {
        _ = (string)p; // Warning, narrowing
        _ = (string)p!; // No warning, suppressed
    }
}

終了サンプル

: 入れ子になった null 許容注釈が異なる型

#nullable enable
public class C
{
    public void M1((string, string) p)
    {
        _ = ((string?, string?))p; // No warning, widening
    }

    public void M2((string?, string?) p)
    {
        _ = ((string, string))p; // Warning, narrowing
        _ = ((string, string))p!; // No warning, suppressed
    }
}

終了サンプル

コンパイラは、型変換に対して警告を発行するかどうかを判断する際に、インターフェイス分散 (§18.2.3.3)、デリゲート分散 (§20.4)、配列共分散 (§17.6) の規則に従うことができます。

#nullable enable
public class C
{
    public void M1(IEnumerable<string> p)
    {
        IEnumerable<string?> v1 = p; // No warning
    }

    public void M2(IEnumerable<string?> p)
    {
        IEnumerable<string> v1 = p; // Warning
        IEnumerable<string> v2 = p!; // No warning
    }

    public void M3(Action<string?> p)
    {
        Action<string> v1 = p; // No warning
    }

    public void M4(Action<string> p)
    {
        Action<string?> v1 = p; // Warning
        Action<string?> v2 = p!; // No warning
    }

    public void M5(string[] p)
    {
        string?[] v1 = p; // No warning
    }

    public void M6(string?[] p)
    {
        string[] v1 = p; // Warning
        string[] v2 = p!; // No warning
    }
}

終了サンプル

バリアント変換を許可しない型で null 値の許容がどちらの方向にも異なる場合、コンパイラは警告を発行することがあります。

#nullable enable
public class C
{
    public void M1(List<string> p)
    {
        List<string?> v1 = p; // Warning
        List<string?> v2 = p!; // No warning
    }

    public void M2(List<string?> p)
    {
        List<string> v1 = p; // Warning
        List<string> v2 = p!; // No warning
    }
}

終了サンプル

条件付き規範テキストの末尾