トップ回答者
非同期ラムダ式のコードの書き方について

質問
-
お世話になります。
非同期ラムダ式のコードの記述方法について教えて下さい。
ためしに、テストで以下のコードを書き実行したところ、以下の結果になりました。
【テストコード】
private void btn_1_Click(object sender, EventArgs e)
{
Func<string, Task<string>> fc = async (txt) =>
{
await Task.Run(() =>
{
Task.Delay(1000).Wait();
});Console.WriteLine("★");
return txt;
};
Console.WriteLine(fc("finish"));
}【結果】
System.Threading.Tasks.Task`1[System.String]
★本来、希望した結果は
★→Finishの順番なのですか、思うような結果になりません。
ためしにConsole.WriteLine(fc("finish"))をConsole.WriteLine(fc("finish").Status)にしたところ
「WaitingForActivation」でした。
非同期ラムダ式を使って、本来、ほしい結果を出すにはどうしたらよろしいでしょうか。
それと、もう1件、Actionの戻り値無しのエラー取得についてお聞きします。
C#で書いた以下のコードと同じ構成でVBで記述しましたが、エラーを受け取れません。
どうすれば受け取れるでしょうか。
【テストコードC#】
Action<string> ac = async (txt) =>
{
await Task.Run(() =>
{
Console.WriteLine(int.Parse(txt));}).ContinueWith((c) => {if (c.Exception != null)
{
Console.WriteLine("【ContinueWith】" + c.Exception.InnerException.Message);
}
});Console.WriteLine(txt);
};
ac("Finish");【結果】
【ContinueWith】入力文字列の形式が正しくありません。
Finish↑
これをVBで、同じ結果を出力したい。
以上、よろしくお願いします。
回答
-
1つ目、fcの返値はTask<string>なので、そのタスクの結果を受け取りたいならawaitする必要があります。
async void btn_1_Click(...) { // ここにasyncが必要 // fcの定義略 Console.WriteLine(await fc("finish")); // fcが返すTask<string>をawait }
2つ目、なぜわざわざContinueWithを使っているのでしょうか。await周りをTry-Catchしたほうがいいと思います。Dim ac As Action(Of String) ac = Async Sub(txt) Try Await Task.Run(Sub() Cnosole.WriteLine(Int32.Parse(txt))) Catch ex As Exception Console.WriteLine("【catch】" + ex.Message) End Try Console.WriteLine(txt) End Sub ac("Finish")
非同期処理があるなら、返値は常にTaskにしておく(つまり、Action<T>ではなくFunc<T, Task>にする)ことをお勧めします。- 回答としてマーク mexicanafro 2020年11月13日 1:44
すべての返信
-
1つ目、fcの返値はTask<string>なので、そのタスクの結果を受け取りたいならawaitする必要があります。
async void btn_1_Click(...) { // ここにasyncが必要 // fcの定義略 Console.WriteLine(await fc("finish")); // fcが返すTask<string>をawait }
2つ目、なぜわざわざContinueWithを使っているのでしょうか。await周りをTry-Catchしたほうがいいと思います。Dim ac As Action(Of String) ac = Async Sub(txt) Try Await Task.Run(Sub() Cnosole.WriteLine(Int32.Parse(txt))) Catch ex As Exception Console.WriteLine("【catch】" + ex.Message) End Try Console.WriteLine(txt) End Sub ac("Finish")
非同期処理があるなら、返値は常にTaskにしておく(つまり、Action<T>ではなくFunc<T, Task>にする)ことをお勧めします。- 回答としてマーク mexicanafro 2020年11月13日 1:44
-
何か目的があってそれを実現するためのコードの一部だと思いますが、そもそも何がしたいのかの目的を書けませんか。
btn_1_Click と書いてあるところを見ると GUI アプリのようですが Console.WriteLine を使っていたり(GUI アプリとコンソールアプリは SynchronizationContext などが違います)、await を使っているのに async がなかったり、await Task.Run(() => {Task.Delay(1000).Wait(); }); とか訳の分からないコードがあったりと、はっきり言わせていただけるとコメントしても意味がなさそうとしか自分には思えないのです。
そもそもの目的が分かれば、その方向でどうできるかのコメントができるかもしれません。(できなかったらすみませんが)それから、何を作っているか(Windows Forms? WPF? ASP.NET Web アプリ? その他?)と開発環境(OS, .NET Framework, Visual Studio のバージョンなど)も書いてください。。
- 編集済み SurferOnWww 2020年11月13日 1:27 追記
-
> 非同期ラムダ式をネットで見まして、例外等が発生したらどこで受け取るんだろうと思いテストしていたところ、上記の問題が出まして。
ラムダ式と普通のメソッドの違いは、少なくとも本質的・基本的なところに関しては関係なさそうです。非同期で動くメソッドで発生した例外は await, async, Task をきちんと使っていれば呼び出し側で捕捉できます。
というわけで、ラムダ式うんぬんよりも、await, async, Task の使い方を勉強した方が良いと思います。
例えば昔のデリゲートを利用した非同期メソッドを使った場合、例外はコールバックもしくは集約的例外ハンドラの中でしか捕捉できませんが、.NET 4.5 以降であれば await, async, Task を使って呼び出し側で捕捉できます。具体例は以下の記事を見てください。
デリゲートを利用した非同期メソッドの実装
http://surferonwww.info/BlogEngine/post/2019/06/19/coding-asynchronous-method-by-using-delegate-in-windows-forms-application.aspx以下の記事も参考になると思いますので、読むことをお勧めします。
非同期プログラミングのベスト プラクティス
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
> Windows Form、Visual Studioのバージョンはコミュニティの2019になります。(学習用環境).NET Framework のバージョンも重要な情報ですので必ず書くようにしてください。Core の場合はそのこととバージョンも書いてください。