2013-07-24 62 views
5

我最近第一次使用異步(和.Net 4.5),我遇到了一些讓我難堪的東西。關於在網上可以找到的VoidTaskResult類沒有太多的信息,所以我來這裏看看是否有人對發生了什麼有什麼想法。什麼是VoidTaskResult類型與異步方法有關?

我的代碼如下所示。顯然,這非常簡單。基本的想法是調用異步的插件方法。如果他們返回任務,那麼異步調用沒有返回值。如果他們返回任務<>,那麼有。我們事先並不知道它們是哪種類型,因此我們的想法是使用反射來查看結果的類型(如果類型爲<>,則IsGenericType爲true)並使用動態類型獲取該值。

在我的真實代碼中,我通過反射調用了插件方法。我認爲這不會對我所看到的行爲產生影響。

// plugin method 
public Task yada() 
{ 
// stuff 
} 

public async void doYada() 
{ 
    Task task = yada(); 
    await task; 

    if (task.GetType().IsGenericType) 
    { 
    dynamic dynTask = task; 
    object result = dynTask.Result; 
    // do something with result 
    } 
} 

這適用於上面顯示的插件方法。 IsGenericType爲false(如預期)。

但是如果你改變了插件的方法的聲明非常輕微,IsGenericType現在返回true,東西遊:

public async Task yada() 
{ 
// stuff 
} 

當你做到這一點,下面的異常被拋出就行了(對象result = dynTask.Result):

RuntimeBinderException

如果你深入到任務對象,它實際上似乎是類型。 VoidTaskResult是線程名稱空間中的私有類型,幾乎沒有任何內容。

VoidTaskResult task

我試圖改變我的調用代碼:

public async void doYada() 
{ 
    Task task = yada(); 
    await task; 

    if (task.GetType().IsGenericType) 
    { 
    object result = task.GetType().GetProperty("Result").GetMethod.Invoke(task, new object[] { }); 
    // do something with result 
    } 
} 

這種「成功」,在某種意義上說,它不會再發生,但現在的結果是類型爲「VoidTaskResult」的,我不能理智做任何事情。

我應該補充說,即使爲這一切制定真正的問題,我也很難。也許我真正的問題是「什麼是VoidTaskResult?」,或者「爲什麼動態調用異步方法時發生這種奇怪的事情?」甚至可能「如何調用可選異步的插件方法?」無論如何,我正在把這件事放在那裏,希望其中一位大師能夠闡明一些事情。

回答

9

這是由於設計圍繞任務(特別是任務完成源)的類層次結構的方式。

首先,Task<T>派生自Task。我假設你已經熟悉這一點。

此外,您可以爲執行代碼的任務創建類型TaskTask<T>。例如,如果你的第一個例子返回Task.Run或什麼,那麼這將返回一個實際的Task對象。

當您考慮如何TaskCompletionSource<T>與任務層次結構交互時,問題就出現了。TaskCompletionSource<T>用於創建不執行代碼的任務,而是用作某個操作已完成的通知。例如,超時,I/O包裝或方法。

沒有非通用TaskCompletionSource類型,所以,如果你想有一個像這樣的通知沒有返回值(例如,超時或async Task方法),那麼你必須創建一些T一個TaskCompletionSource<T>並返回Task<T>async團隊必須爲async Task方法選擇T,因此他們創建了VoidTaskResult類型。

通常這不是問題。由於Task<T>來自Task,因此該值會轉換爲Task並且每個人都很高興(在靜態世界中)。但是,由TaskCompletionSource<T>創建的每個任務實際上都是Task<T>類型,而不是Task,並且您可以通過反射/動態代碼來看到它。

最終結果是,您必須像Task一樣對待Task<VoidTaskResult>。但是,VoidTaskResult是一個實現細節;它可能會在未來發生變化。

因此,我建議您實際上將您的邏輯基於(聲明的)返回類型yada而不是(實際)返回值。這更接近模仿編譯器的功能。

Task task = (Task)yadaMethod.Invoke(...); 
await task; 

if (yadaMethod.ReturnType.IsGenericType) 
{ 
    ... 
} 
+0

爲什麼異步團隊引入VoidTaskResult而不是引入非泛型的TaskCompletionSource? – Puppy

+0

@Puppy:我懷疑是因爲它的工作量少了很多。編寫一個非泛型'TaskCompletionSource'並不難,但任何公共API必須經過一堆其他「門」才能發佈。安全評論等等。 –

相關問題