質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Q&A

解決済

3回答

22320閲覧

コンストラクタでの非同期メソッドの実行

shiita

総合スコア16

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

0グッド

0クリップ

投稿2016/09/19 12:19

C# Xamarinでアプリの開発をしています。

「PCL Storage」というプラグインを使用しているのですが、その中のメソッドのGetFilesAsyncとReadAllTextAsyncをコンストラクタで使用したいと思っています。ですが、このメソッドは非同期のもので、コンストラクタで使用することができなくて困っています。

C#

1 public class TaskListView : ListView 2 { 3 public TaskListView() 4 { 5 var data = makeTaskListData(); 6 ItemsSource = data.Result; 7 8 var cell = new DataTemplate(typeof(TextCell)); 9 cell.SetBinding(TextCell.TextProperty, "Title"); 10 11 ItemTemplate = cell; 12 } 13 14 // 非同期メソッドで実装するしかなかった。ListViewのデータクラス 15 private async Task<List<TaskData>> makeTaskListData() 16 { 17 IFolder rootFolder = FileSystem.Current.LocalStorage; 18 IList<IFile> fileList = await rootFolder.GetFilesAsync(); 19 var fileTitleList = new List<TaskData>(); 20 21 foreach (var file in fileList) 22 { 23 string fileTitle = await file.ReadAllTextAsync(); 24 // とりあえずタイトルを取得 25 fileTitleList.Add(new TaskData { Title = fileTitle }); 26 } 27 return fileTitleList; 28 } 29 }

このようにprivateのメソッドを作り、そこに非同期処理を纏めてから、コンストラクタで使用するという方法もやってみたのですが、このListViewを表示しようとするとフリーズしてしまいました。

根本的にやり方が間違っているような気がします。質問の内容以外でもおかしな所があれば、指摘していただけると嬉しいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

コンストラクタ内のdata.Resultは結果が返ってくるまでUIスレッドを止めてしまいます。
makeTaskListData内でawait rootFolder.GetFilesAsync();が非同期に処理され、それがUIスレッドのコンテキストに戻されるけれど、すでにUIスレッドは結果待ちで止まっているのでそこできれいにデッドロックしてるかと。
kekyo2さんがasync-awaitダークサイドis何にまとめられていますのでご参考にされてみてはいかがでしょうか。

そちらにあるようにConfigureAwaitなどを使えばフリーズしないのではないかと思いますが、TaskをResultなどで待つことはそのスレッドを止めてしまうため基本的にはしない方がいいと思います。

ちなみにそのような場合はコンストラクタは最低限のクラスの初期化だけすまして別にasyncのInitializeメソッドを設けそれを使って非同期の処理を呼び出すなども手かと思います。

投稿2016/09/19 15:52

編集2016/09/19 15:55
omanuke

総合スコア109

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shiita

2016/09/20 05:48

awaitとasyncを付けていれば非同期で動いてくれるんだな、程度の理解しかなかったため、非常に参考になりました。ありがとうございます。 ConfigureAwait(false)にすることで、ワーカースレッドにmakeTaskListData()を最後まで実行させることで、Resultで結果を待ってもデッドロックしなくなりました。
guest

0

C#に詳しいわけではないですが,調べてみると「やっぱりそうか」と思える手法は見つかりました.staticなファクトリメソッドからprivateなコンストラクタを叩くようにすると良さそうです.

c# - Can constructors be async? - Stack Overflow

追記

JavaScriptのノリで適当に書いてみました,あやふやな知識で書いた上にデバッグも全くしてないので,おかしいところがあれば指摘お願いします.

C#

1public class TaskListView : ListView 2{ 3 public static async Task<TaskListView> BuildTaskListViewAsync() 4 { 5 var rootFolder = FileSystem.Current.LocalStorage; 6 var files = await rootFolder.GetFilesAsync(); 7 var taskDataList = await Task.WhenAll(files.Select(async file => { 8 var title = await file.ReadAllTextAsync(); 9 return new TaskData { Title = title }; 10 })); 11 return new TaskListView(taskDataList); 12 } 13 14 private TaskListView(List<TaskData> taskDataList) 15 { 16 ItemsSource = taskDataList; 17 var cell = new DataTemplate(typeof(TextCell)); 18 cell.SetBinding(TextCell.TextProperty, "Title"); 19 ItemTemplate = cell; 20 } 21}

投稿2016/09/19 13:16

編集2016/09/19 14:42
mpyw

総合スコア5223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mpyw

2016/09/19 13:48

ところで元のコード,ファイルの読み取りを1個ずつawaitしてるの無駄じゃないでしょうか?「ファイル内容を読み取ってreturnするタスク」をリストtasksにあらかじめ全部突っ込んでおいて,Task.WhenAll(tasks) をawaitすればいいと思います. (C#には詳しくないので正確なコードを書く自信はないです…
shiita

2016/09/20 05:40

わざわざコードまでありがとうございます。Task.WhenAll知りませんでした、無駄を省けました。Task.WhenAllの戻り値が配列のタスクだったため、リストにキャストしたら動くようになりました。 TaskListViewのコンストラクタをタスクで返せたのはいいのですが、TaskListViewを使う場所も別のクラスのコンストラクタなので、フリーズは回避できませんでした。
guest

0

単なるクラスなら以下のやり方とかあると思います。
1.コンストラクタで何もせずInitializeAsyncメソッドで取り込む
2.staticなCreateInstanceAsyncメソッドでクラスを作って返す

が、これはListView(GUI部品)なのでomanukeさんの言うように根本的な作り方としてまずいです。
MVVM的に分離して作られてはいかがでしょうか?

投稿2016/09/20 05:28

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

shiita

2016/09/20 05:56

回答ありがとうございます。GUIプログラミングとC#を勉強し始めて日が浅いため、MVVM(特にデータバインディング)がよくわかっていないです。omanukeさんの回答から動くものは完成したので、後々勉強をしながら改良していきたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問