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

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

ただいまの
回答率

90.34%

  • C#

    7707questions

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

  • Xamarin

    532questions

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

  • 非同期処理

    115questions

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

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

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 4,458

shiita

score 10

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

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

public class TaskListView : ListView
    {
        public TaskListView()
        {
            var data = makeTaskListData();
            ItemsSource = data.Result;

            var cell = new DataTemplate(typeof(TextCell));
            cell.SetBinding(TextCell.TextProperty, "Title");

            ItemTemplate = cell;
        }

        // 非同期メソッドで実装するしかなかった。ListViewのデータクラス
        private async Task<List<TaskData>> makeTaskListData()
        {
            IFolder rootFolder = FileSystem.Current.LocalStorage;
            IList<IFile> fileList = await rootFolder.GetFilesAsync();
            var fileTitleList = new List<TaskData>();

            foreach (var file in fileList)
            {
                string fileTitle = await file.ReadAllTextAsync();
                // とりあえずタイトルを取得
                fileTitleList.Add(new TaskData { Title = fileTitle });
            }
            return fileTitleList;
        }
    }

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/20 14:48

    awaitとasyncを付けていれば非同期で動いてくれるんだな、程度の理解しかなかったため、非常に参考になりました。ありがとうございます。

    ConfigureAwait(false)にすることで、ワーカースレッドにmakeTaskListData()を最後まで実行させることで、Resultで結果を待ってもデッドロックしなくなりました。

    キャンセル

+2

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

c# - Can constructors be async? - Stack Overflow

 追記

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

public class TaskListView : ListView
{
    public static async Task<TaskListView> BuildTaskListViewAsync()
    {
        var rootFolder = FileSystem.Current.LocalStorage;
        var files = await rootFolder.GetFilesAsync();
        var taskDataList = await Task.WhenAll(files.Select(async file => {
            var title = await file.ReadAllTextAsync();
            return new TaskData { Title = title };
        }));
        return new TaskListView(taskDataList);
    }

    private TaskListView(List<TaskData> taskDataList)
    {
        ItemsSource = taskDataList;
        var cell = new DataTemplate(typeof(TextCell));
        cell.SetBinding(TextCell.TextProperty, "Title");
        ItemTemplate = cell;
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/19 22:48

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

    (C#には詳しくないので正確なコードを書く自信はないです…

    キャンセル

  • 2016/09/20 14:40

    わざわざコードまでありがとうございます。Task.WhenAll知りませんでした、無駄を省けました。Task.WhenAllの戻り値が配列のタスクだったため、リストにキャストしたら動くようになりました。

    TaskListViewのコンストラクタをタスクで返せたのはいいのですが、TaskListViewを使う場所も別のクラスのコンストラクタなので、フリーズは回避できませんでした。

    キャンセル

+1

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/20 14:56

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

    キャンセル

同じタグがついた質問を見る

  • C#

    7707questions

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

  • Xamarin

    532questions

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

  • 非同期処理

    115questions

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