94583


# 開発環境

  * .NET 6
  * Visual Studio Code/Visual Studio 2022

# 質問内容

コールバックが実行されるまで待機し、その戻り値を返すメソッドを開発しています。  
細かい部分は省略していますが、以下が現段階でのコードになります。

[code]

    public TResult Invoke<TState, TResult>(Func<TState, TResult> callback, TState state)
    {
        // CS8603回避のため配列にしています。
        TResult[] result = new TResult[1];
        using ManualResetEventSlim resetEvent = new ();
        // InvokeAsyncは、非同期にコールバックを実行するメソッドです。
        InvokeAsync(
            () =>
            {
                try
                {
                    result[0] = callback(state);
                }
                finally
                {
                    resetEvent.Set();
                }
            });
        resetEvent.Wait();
        return result[0];
    }
    
[/code]

最初はローカル変数を`TResult? result =
default`としていたのですが、どこかで[CS8603](https://learn.microsoft.com/ja-
jp/dotnet/csharp/language-reference/compiler-messages/nullable-
warnings#possible-null-assigned-to-a-nonnullable-
reference)の警告が出るのを回避できなかったため、やむを得ず配列を使用しています。  
配列を使用せずに、CS8603を回避する方法はないでしょうか。  
ただし、以下の方法はメソッドの仕様が変わってしまうので避けたいです。

  * 戻り値の型を`TResult`ではなく`TResult?`に変更する。
  * `TResult`に制約をつける。

## 追記

以下のようにしても警告は出てしまうのですね。  
[C# コンパイラによって解釈される null 状態のスタティック分析の属性](https://learn.microsoft.com/ja-
jp/dotnet/csharp/language-reference/attributes/nullable-
analysis)で何かコンパイラに問題ないことを伝える手段があれば良いのですが。  
それともまだ抜けがあるのでしょうか？

[code]

    public TResult Invoke<TState, TResult>(Func<TState, TResult> callback, TState state)
    {
        TResult? result = default;
        Action action = () => result = callback(state);
        action(); // callbackで例外が発生した場合はreturnまで行かないので、代入は行われているはず。
        return result; // CS8603
    }
    
[/code]


CC BY-SA 4.0
2023-04-19T14:58:01.647
94578
2023-04-26T13:49:48.470
2023-04-20T01:53:31.383
70
70
question
c#
配列を使わずにCS8603を回避したい


はじめに「警告を回避したい」という考え方はお勧めできません。警告内容に真摯に向き合い、適切に対応するべきです（その場合は「回避」という表現にならないはずです）。

`TResult? result = default`
とした場合になぜ警告されるかを把握する必要があります。`InvokeAsync()`は第１引数のコールバックを呼ぶ保証がありません。呼ばなければ`result`は`default`つまり`null`のままです。もしくはコールバックを呼んだとして代入前に例外が発生すれば`result`は`null`のままです。  
いずれかが発生すると`Invoke()`は`null`を返してしまいますので戻り値型`TResult`（`null`を返さない）に違反します。  
C#コンパイラは問題を的確に見抜いて警告していることが分かります。

配列を使った場合は、コンパイラのチェックを騙しているだけであり、`null`を返してしまう問題そのものは何も解決していません。文字通り「CS8603を回避」でしかありません。

メソッドの仕様が変わってしまう変更を避けたいのであれば、nullチェックを止めるしかありません。


CC BY-SA 4.0
2023-04-19T19:28:45.923
94581
2023-04-19T19:28:45.923
4236
94578
answer


質問者さんが言う警告はこれですよね？

[![画像の説明をここに入力](https://i.stack.imgur.com/nWUyP.jpg)](https://i.stack.imgur.com/nWUyP.jpg)

質問者さんが result は null
にならないようにコーディングしたということかもしれませんが、コンパイラはそれは分からないので警告を出したということです。

result は null にならないことが保証できるのであれば、null 免除演算子を使って警告が出ないようにできます。

! (null 免除) 演算子 (C# リファレンス)  
<https://learn.microsoft.com/ja-jp/dotnet/csharp/language-
reference/operators/null-forgiving>

以下の通りです。

[![画像の説明をここに入力](https://i.stack.imgur.com/HAHTB.jpg)](https://i.stack.imgur.com/HAHTB.jpg)


CC BY-SA 4.0
2023-04-20T02:11:35.737
94583
2023-04-20T02:11:35.737
51338
94578
answer


null免除演算子でも目的は達成できたのですが、最終形は[MaybeNull属性](https://learn.microsoft.com/ja-
jp/dotnet/api/system.diagnostics.codeanalysis.maybenullattribute?view=net-7.0)を戻り値に付与する形を取ることにしました。  
これはまさにジェネリックで`TResult?`と書けない事情がある場合の回避方法として紹介されていました。  
具体的には以下のようなコードです。

[code]

    [return: MaybeNull]
    public TResult Invoke<TState, TResult>(Func<TState, TResult> callback, TState state)
    {
        TResult? result = default;
        // 何かの処理
        return result;
    }
    
[/code]

null免除演算子では`TResult`に`null`が入ることがないと誤解される可能性があるため、`TResult`が`string?`の場合などに`null`が返される可能性があるという意味を込められるMaybeNull属性を選択させていただきました。

参考1：[C# コンパイラによって解釈される null 状態のスタティック分析の属性](https://learn.microsoft.com/ja-
jp/dotnet/csharp/language-reference/attributes/nullable-analysis)  
参考2：[null
許容参照型](https://ufcpp.net/study/csharp/resource/nullablereferencetype/?p=4)


CC BY-SA 4.0
2023-04-26T13:49:48.470
94656
2023-04-26T13:49:48.470
70
94578
answer