やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

C#8.0 #nullableディレクティブのrestoreはプロジェクト設定に戻す

 ということを確認した。

成果物

nullコンテキスト

コンテキスト設定

設定方法コンテキストnull許容
<Nullable>注釈警告参照型
enable
warnings
annotations
disable
設定方法コンテキスト
#nullable注釈警告
enable
disable
restorepjpj
enable warnings
disable warnings
restore warningspj
enable annotations
disable annotations
restore annotationspj

 公式によるとrestoreは「プロジェクト設定に戻す」らしいが、ここでは「1つ前の設定に戻す」とある。どっちだよ。

 以下、設定例。

.csproj

<Nullable>enable<Nullable>

.cs

#nullable enable
...
#nullable disable

プロジェクト作成

dotnet new console -o NullableDirective
cd NullableDirective

.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

</Project>

 プロジェクト生成したときのままで変更なし。<Nullable>の追加もせず。

コード

Program.cs

new Enable().Run();
new Disable().Run();
new Restore().Run();

Enable.cs

class Enable
{
    public void Run()
    {
#nullable enable
            int i1 = null; // error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません
            int? i2 = null;
            string s1 = null; // warning CS8600: Null リテラルまたは Null の可能性がある値を Null 非許容型に変換しています。
            string? s2 = null;
            string s3 = null!;
            Console.WriteLine($"{i1}, {i2}, {s1}, {s2}, {s3}");
    }
}

Disable.cs

class Disable
{
    public void Run()
    {
#nullable disable
            int i1 = null; // error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません
            int? i2 = null;
            string s1 = null;
            string? s2 = null; // warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。
            Console.WriteLine($"{i1}, {i2}, {s1}, {s2}");
    }
}

Restore.cs

class Restore
{
    public void Run()
    {
#nullable restore
        int i1 = null;
        int? i2 = null;
        string s1 = null;
        string? s2 = null;
        Console.WriteLine($"{i1}, {i2}, {s1}, {s2}");
    }
}

実行結果

$ dotnet run
Enable.cs(10,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません [/tmp/work/CSharp.Tutorial.Nullable.Directive.20191030134014/src/NullableDirective/NullableDirective.csproj]
Enable.cs(12,25): warning CS8600: Null リテラルまたは Null の可能性がある値を Null 非許容型に変換しています。 [/tmp/work/CSharp.Tutorial.Nullable.Directive.20191030134014/src/NullableDirective/NullableDirective.csproj]
Restore.cs(10,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません [/tmp/work/CSharp.Tutorial.Nullable.Directive.20191030134014/src/NullableDirective/NullableDirective.csproj]
Restore.cs(13,19): warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。 [/tmp/work/CSharp.Tutorial.Nullable.Directive.20191030134014/src/NullableDirective/NullableDirective.csproj]
Disable.cs(10,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません [/tmp/work/CSharp.Tutorial.Nullable.Directive.20191030134014/src/NullableDirective/NullableDirective.csproj]
Disable.cs(13,19): warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。 [/tmp/work/CSharp.Tutorial.Nullable.Directive.20191030134014/src/NullableDirective/NullableDirective.csproj]

ビルドに失敗しました。ビルド エラーを修正して、もう一度実行してください。

 以下のコンパイルエラーに関しては共通だった。

int i1 = null;
Restore.cs(10,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません
Enable.cs(10,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません
Disable.cs(10,22): error CS0037: Null 非許容の値型であるため、Null を 'int' に変換できません

 Restoreの結果はDisableと同じだった。

Enable.cs

null許容型に対する警告なし

 参照型でnull許容したいときは#nullable enableすると警告なく書けるようだ。

 .csprojに<Nullable>を設定しなくても#nullable enableディレクティブ範囲内なら以下CS8632警告が出ない。

warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。
string? s2 = null;
  • #nullable enableにする
    • 参照型をnull許容型にする
      • 警告CS8632が出ない

null非許容型に対する警告あり

 null非許容のときにnullが代入されたらCS8600警告が出る。

Enable.cs(12,25): warning CS8600: Null リテラルまたは Null の可能性がある値を Null 非許容型に変換しています。
string s1 = null;

null免除演算子!で警告回避

 上記コードのnull!を追加すればCS8600警告を回避できる。つまり、null非許容型にnull!を代入しても警告が出ない。nullだと出る。

string s1 = null!; // 警告CS8600なし

Restore.cs

#nullable restoreはプロジェクト設定に戻す

 前回#nullable restoreの効果がわからなかった。公式によると「プロジェクト設定に戻す」らしいが、ここでは「1つ前の設定に戻す」とある。どっちかわからなかった。

 今回試してみたところ、Restoreプロジェクト設定に戻すものだと思われる。

 前提条件は以下。

  • プロジェクトは<Nullable>未設定のため#nullable disableと同様のはず
  • 今回のコードにおいてenable, disable, restoreの順に実行した

 Restore時点ではその直前にあたる#nullable disableの状態なはず。それの1つ前だと#nullable enableである。つまり、1つ前ならEnable実行結果と同じになるはず。だが、実際にはDisable実行結果と同じになった。

 プロジェクト設定は<Nullable>未設定のため#nullable disableと同様のはずである。これが実際の結果と一致した。

 つまり、#nullable restoreの意味は公式通り、「プロジェクト設定に戻す」という意味なのだろう。

 あースッキリした。

検証ログ抜粋

Restore.cs(13,19): warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。 
Disable.cs(13,19): warning CS8632: '#nullable' 注釈コンテキスト内のコードでのみ、Null 許容参照型の注釈を使用する必要があります。

 各ソースコードにおける13行目は以下。

string? s2 = null;

 同一コードだが、Enable.csでは警告が出ていない。なのにDisable.csRestore.csは出ている。つまりそれらは同じ動作をしたということだろう。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux