情報応用演習Ⅰ(2024)

【T2b】新しいNull許容/非許容型(4/6)

プロジェクトタイプC#コンソールアプリ※
プロジェクト名T2b
ソリューション名PIT2
ターゲットフレームワーク.NET 8.0(長期的なサポート)
最上位レベルのステートメントを使用しない使用する(チェックオフ)

※「コンソールアプリ(.NET Framework)」ではないので注意せよ!

注意
  • 本ページの作業内容は 前のページまでの続き になっていることに注意せよ.
    • 先に前のページまでをすべて読み,指示されている作業を済ませてから本ページを読むこと.
    • プロジェクトの作成作業についてはNull許容参照型を参照せよ.

2b-4. Null許容型とそうでない型の互換性

従来の値型のNull許容型は,Null許容ではない─つまりNull許容の普通の値型の変数に直接代入することはできなかった. 復習のためにこのことも確認しておこう.Program.csに_に示す内容を追記する.

Program.csの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
string? hoge = null;

Console.WriteLine(hoge);

int? piyo = null;

if (piyo.HasValue)
{
    Console.WriteLine(piyo.Value);
}

if (hoge != null)
{
    Console.WriteLine(hoge);
}

int foo = 0;     // Null許容型ではない
                 // 普通の int 型の変数 foo を宣言する.

foo = piyo;      // ↑の変数 foo に Null許容 int 型の
                 // 変数 piyo の値を代入しようとする(エラーになる).

17行目でNull許容型ではない普通の int 型の変数fooを宣言して, 20行目ではこの変数に,先ほど宣言した int? 型の変数piyoの値(≒中身)を代入しようとしている. _に示す通り,これはエラーとなる記述である.

記述結果

このようにNull許容な式を,そうではない普通の値型の変数などに代入することはできない. では今回議論しているNull許容参照型で同じことをしようとするとどうなるか確認してみよう. Program.csに_に示す内容を追記する.

Program.csの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
string? hoge = null;

Console.WriteLine(hoge);

int? piyo = null;

if (piyo.HasValue)
{
    Console.WriteLine(piyo.Value);
}

if (hoge != null)
{
    Console.WriteLine(hoge);
}

int foo = 0;

foo = piyo;

string bar = "xyzzy"; // Null許容型ではない
                      // 普通の string 型の変数 bar を宣言する.

bar = hoge;           // ↑の変数 bar に Null許容 string 型の
                      // 変数 hoge の値を代入しようとする(警告がでる).

21行目でNull許容型ではない普通の string 型の変数barを宣言して, 24行目ではこの変数に,先ほど宣言した string? 型の変数hogeの値(≒中身)を代入しようとしている.

これはエラーにこそならないものの警告が表示される.Visual Studio のエディタ上は緑色の波線が現れているはずである. _に示す通り,「ここでは、'hoge'は null である可能性があります。 」と警告されるはずである.

記述結果

では24行目の代入文を_に示すようなif文で囲むとどうなるだろうか. Program.csに_に示す内容を追記する.

Program.csの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
string? hoge = null;

Console.WriteLine(hoge);

int? piyo = null;

if (piyo.HasValue)
{
    Console.WriteLine(piyo.Value);
}

if (hoge != null)
{
    Console.WriteLine(hoge);
}

int foo = 0;

foo = piyo;

string bar = "xyzzy";

if (hoge != null)     // 事前にif文などでNull許容型の変数が
{                     // nullではないことを確認していれば,
    bar = hoge;       // ←のような代入を行っても
}                     // 警告はされない.

今度は_に示す通り,Null許容な式をそうではない普通の参照型の変数に代入しても警告はされなかったはずである.

記述結果

このようにC#にはプログラムの流れを追跡してNull許容ではない通常の参照型に対する null の代入といった操作を警告する 機能がある. これは「 null が入った変数を操作してしまう」といった,よくある誤りを低減するための警告である. 読者らも変数の中身が空( null )の変数を操作してしまうようなプログラムを書いて,実行時エラー (NullReferenceException例外)を発生させてしまったことが何度もあるのではないだろうか. この種の誤りは非常に発生しやすいため,現在の C# では nullの関与する操作に関してより厳格になっている

なお,値型のNull許容型に関しては,そうではない普通の値型とは型レベルで交換性がないため,たとえif文などで 事前にチェックしていたとしても従来通りエラーとなる.このことも確かめておこう.Program.csに_に示す内容を追記する.

Program.csの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
string? hoge = null;

Console.WriteLine(hoge);

int? piyo = null;

if (piyo.HasValue)
{
    Console.WriteLine(piyo.Value);
}

if (hoge != null)
{
    Console.WriteLine(hoge);
}

int foo = 0;

if (piyo != null)     // 値型のNull許容でも「○○ != null」のようにして
{                     // チェックは可能である(意味的には .HasValue と同じ).
    foo = piyo;       // しかし,こちらはエラーとなるので従来通り
}                     // .Value を使用する必要がある.

string bar = "xyzzy";

if (hoge != null)
{
    bar = hoge;
}

_に示す通り,値型に関しては null チェックをしていたとしてもエラーとなる.これは 前述した通り int 型と int? 型は互換性のない型であるためである.

記述結果

コメントにもあるように,Null許容な値型を,普通の値型の変数に代入するには従来通り.Valueプロパティを使用する必要がある. Program.csを_に示す通り修正しよう.

Program.csの変更内容
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
string? hoge = null;

Console.WriteLine(hoge);

int? piyo = null;

if (piyo.HasValue)
{
    Console.WriteLine(piyo.Value);
}

if (hoge != null)
{
    Console.WriteLine(hoge);
}

int foo = 0;

if (piyo != null)
{
    foo = piyo.Value; // これなら OK
}

string bar = "xyzzy";

if (hoge != null)
{
    bar = hoge;
}

このように,現在の C# では Null許容型の概念が参照型に押し広げられているが,「値型のNull許容型」と「参照型のNull許容型」では 若干セマンティクス1が異なるので注意が必要である.

ここまでを理解したら次に進もう.


  1. semantics...意味論.ここでは記述方法(syntax(しんたっくす))に対して,その記述によってもたらされる動作といったニュアンス. ↩︎

Last updated on 2024-04-26
Published on 2024-04-26

Powered by Hugo. Theme by TechDoc. Designed by Thingsym.