ConsoleAppFrameworkでasync voidなコマンドは実行しても正常に完了できません。async Taskを使いましょう。以上です。
どのような挙動になるかを説明しておきます。
ConsoleAppFrameworkのCommand
ConsoleAppFrameworkのコマンドの返り値は次のいずれかを期待しています。エラー時は例外にするならvoidやasync Task、ExitCodeを調整したいときはintやasync Task<int>を使うという使い分けです。
voidasync Taskintasync Task<int>
たとえば同期処理しかないコマンドは次のように書けます。
[Command("hello")] public void Hello(string name) { Console.WriteLine($"Hello {name}!"); }
$ dotnet run ConsoleApp1 -- hello foo
Hello foo!
たとえば非同期処理を含むコマンドなら次のように書けます。
[Command("hello-async")] public async Task AsyncTask(string name) { var ts = TimeProvider.System.GetTimestamp(); Console.WriteLine($"Hello {name}!"); await Task.Delay(1000); Console.WriteLine($"Waited {TimeProvider.System.GetElapsedTime(ts)}"); }
$ dotnet run ConsoleApp1 -- hello-async foo Hello foo! Waited 00:00:01.0050441
async voidの問題
async Taskと書くつもりで間違えてasync voidにすることはまれに時々あります。1
ではasync voidにしたコマンドはasync Taskとどのように挙動が違うか見てみましょう。
// async Taskをasync voidに変更しただけ [Command("hello-async-void")] public async void AsyncVoid(string name) { Console.WriteLine($"Hello {name}!"); await Task.Delay(1000); Console.WriteLine($"Waited"); }
async voidだと非同期処理が完了する前にコマンドが終了してしまいます。このため、await Task.Delay(1000)を待つことなくコマンドがそこで終了するのでWaited...が表示されません。
$ dotnet run ConsoleApp1 -- hello-async-void foo
Hello foo!
IDEやビルド時に気づければよいのですが、5.3.3まではasync voidなコマンドでも何事もなくビルド、実行できるので気づくのに遅れるでしょう。
また、デバッグ実行してもawaitの行でステップ実行を進めるとデバッガーが終了します。これはasync voidあるあるですね。
対策
async Taskにしましょう。
また、2025/1/16にリリースされた5.3.4でasync voidコマンドはアナライザーがエラーを出してビルドできくなりました。2今ConsoleAppFramework v5を使っている人は、このバージョンにアップグレードしておきましょう。