web-dev-qa-db-ja.com

非同期ラムダ式をデリゲート型System.Func <T>に変換しますか?

このシグネチャを持つポータブルクラスライブラリ内に非同期メソッドがあります。

private async Task<T> _Fetch<T>(Uri uri)

具体的な型Tとしてキャストバックされるリソースをフェッチします。

私は、パラメータの1つとしてFunc<T>を必要とするサードパーティのキャッシュライブラリ( Akavache )を使用しており、次のようにしてみました。

await this.CacheProvider.GetOrCreateObject<T>(key,
    async () => await _Fetch<T>(uri), cacheExpiry);

これはエラーになります:

非同期ラムダ式をデリゲート型 'System.Func<T>'に変換できません。非同期ラムダ式はvoidTaskまたはTask<T>を返す場合がありますが、いずれも「System.Func<T>」に変換できません。

私はFunc<T>割り当てのさまざまな組み合わせを試しましたが、コードを機能させる唯一の方法はFunc<T>をブロックすることです。

await this.CacheProvider.GetOrCreateObject<T>(key, 
    () => _Fetch<T>(uri).Result, cacheExpiry); 

これは私のアプリをデッドロックさせます。

私が迷うところへのポインタはありますか?

28

できません。誰かが_Func<T> f_を期待している場合、result = f()のようなもので呼び出されると想定できます。つまり、非同期の動作については知りません。あなたが持っているように_.Result_を使用してそれをだます場合-UIスレッドでawait(_Fetch内)の後にコードをスケジュールする必要があるため、UIスレッドでデッドロックしますが、すでにブロックしています_.Result_を使用します。

非同期ラムダは、戻り値がないためActionに、または_Func<Task>_または_Func<Task<T>>_に渡すことができます。

あなたのケースを見ると、GetOrCreateObjectGetOrFetchObjectを呼び出しているようです。 GetOrFetchObjectオーバーロードの1つが_Func<Task<T>>_を受け入れます。そのメソッドを非同期ラムダで呼び出して、それが役立つかどうかを確認できます。

18
YK1

YK1の回答 は、Func<T>を非同期として処理できない理由を説明しています。

問題を解決するには、GetOrFetchObjectではなくGetOrCreateObjectを使用してください。 「作成」メソッドは(同期)作成を前提としていますが、「フェッチ」メソッドは(非同期)検索で機能します。

await CacheProvider.GetOrFetchObject<T>(key, () => _Fetch<T>(uri), cacheExpiry)

また、ラムダ式から不要なasync/awaitを削除しました。 _FetchはすでにTask<T>を返すため、そのタスクをasyncすることのみを目的としたawaitラムダを作成する必要はありません。

6
Stephen Cleary