2013-06-21 51 views
18

我有一個便攜式類庫中的異步方法與此簽名:將異步lambda表達式轉換爲委託類型System.Func <T>?

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

它獲取被轉換回爲一個具體類型T.

我與第三方緩存工作的資源庫(Akavache),需要一個Func<T>作爲參數之一,曾嘗試在這種方式這樣做:

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

這導致錯誤:

Cannot convert async lambda expression to delegate type ' System.Func<T> '. An async lambda expression may return void , Task or Task<T> , none of which are convertible to ' System.Func<T> '.

我試過Func<T>分配的各種排列沒有任何的運氣,我可以得到代碼工作的唯一方法是使Func<T>阻塞:

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

這會造成死鎖我的應用程序。

任何指向哪裏我會誤入歧途?

+0

你在找'Func ' –

回答

13

不行。當有人預計Func<T> f時,你可以認爲它會被調用類似result = f()的東西 - 即它不知道異步行爲。如果您像使用.Result一樣欺騙它 - 它將在UI線程上死鎖,因爲它想要在UI線程中的await(in _Fetch)之後計劃代碼,但是您已經用.Result阻止了它。

異步lambda可以傳遞給Action,因爲它沒有返回值 - 或Func<Task>Func<Task<T>>

看着你的情況,GetOrCreateObject看起來叫GetOrFetchObject。其中一個GetOrFetchObject過載接受Func<Task<T>>。你可以嘗試用你的異步lambda來調用這個方法,看看它是否有幫助。

+3

我想強調一下,如果一個方法接受一個'Action'並且你傳遞了一個'async' lambda,那麼大部分時間這就是錯誤的做法。 – svick

+0

@svick:是的,與你同意。就像接受'Func '的方法一樣,接受'Action'的方法不會知道它的異步行爲。不返回任何結果的異步lambda將理想地傳遞給'Func '。它僅僅是C#編譯器允許它傳遞給'Action',因爲沒有返回值。 – YK1

+0

謝謝YK1,我現在覺得很傻 - 我已經實現了一個接口來抽象出緩存提供者,並錯過了基礎庫中的替代方法。 –

-3

這樣的事情?

Public Func<T> ConvertTask<T>(Task<T> task) 
{ 
    return()=>task.Result; 
} 
+0

這個問題清楚地表明,使用'Result'會導致死鎖。 – svick

4

YK1's answer解釋了爲什麼你不能把Func<T>爲異步。

要解決您的問題,請使用GetOrFetchObject而不是GetOrCreateObject。 「創建」方法假定(同步)創建,而「獲取」方法與(異步)檢索一起工作。

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

我還你lambda表達式除去不必要async/await。由於_Fetch已經返回Task<T>,因此不需要創建async lambda,其唯一目的是await該任務。

+0

謝謝斯蒂芬,這正是我最終解決問題的方法。 –

相關問題