2016-01-22 86 views
3

我工作的具有法Action<T>參數類:無效,任務返回方法區分

public void RegisterCallback<T> (Action<T> callback); 

此作品不夠好。

instance.RegisterCallback<string>(Callback); //method group 
instance.RegisterCallback<string>(t => {}); //lambda 

現在我想爲此方法創建一個過載,它接受async方法。所以我可以在任務返回回調中使用它,並以不同的方式處理它們。

instance.RegisterCallback<string>(AsyncCallback); //method group with async 

哪裏AsyncCallback

private Task AsyncCallback(string s) 
    { 
     return Task.Delay(0); 
    } 

天真的做法是有這樣一個方法:

void RegisterCallback<T>(Func<T, Type> callback); 

它雖然也存在一些問題:

//1 
instance.RegisterCallback<string>(async t => await Task.Delay(0)); 
//2 
instance.RegisterCallback<string>(AsyncCallback); //method group with async 

的第一個獲得資源由於不明確的調用而導致第二次編譯失敗。

嗯,這makes sense和我確定這個接口:

void RegisterAsyncCallback<T>(Func<T, Task> callback); 

但是這兩個電話都沒有問題,編譯:

instance.RegisterCallback<string>(async t => await Task.Delay(0)); 
    instance.RegisterAsyncCallback<string>(async t => await Task.Delay(0)); 

有沒有設計這個公共API的方式以便用戶只能使用void回調,一個方法和任務返回另一個。

也許有人可以指點我現有的api,類似的問題解決了嗎?

完整的代碼可以在here找到。

回答

3

這裏沒有好的解決方案;你會有方法重載的模糊調用錯誤,或者你會有一個全新的API。

就個人而言,我會加上Func<T, Task>重載並採取重大更改,不再允許重載方法。或者,您可以在新方法(RegisterCallbackEx?)上創建兩個重載,並將舊的標記爲Obsolete - 這將允許較舊的代碼進行編譯,但鼓勵開發人員更改調用。

也許有人可以指點我現有的api,類似的問題解決了嗎?

那麼,我可以給你一個例子,其中類似的問題不是解決。 :)

Task.Factory.StartNew通常用於將工作排隊到線程池。它基於Action,我相信它適用於方法組。

當.NET團隊想要添加異步支持時,他們引入了一個新的API:Task.Run,它過載爲ActionFunc<Task>。 (請注意,在這種情況下,Task.Run在技術上僅支持StartNew使用的子集,而StartNew未標記爲Obsolete)。

他們考慮了他們的選擇,但這是他們能想到的最好的選擇。請注意,以下問題仍然存在,開發人員在這裏提出有關這兩個問題的疑問:

  • Task.Run不支持方法組。
  • StartNew將與async lambdas編譯得很好,但會表現出驚人的方式(由於async void)。

由於他們改變.NET BCL,向後兼容性是最重要的。對於個人圖書館而言,我更喜歡使用更安全/更清潔的API來實現嚴格的向後兼容性,所以我會在您的情況下做出其他選擇(即,只需添加Func<T, Task>過載作爲主要版本升級的一部分)。

+0

感謝Task.Run的好例子。我正在尋找那樣的東西。 –

2

有沒有一種方法來設計這個公共API,以便用戶將只使用無效回調與一個方法和任務返回與另一個。

不,不是真的。問題是Action<T>代表。它已經發現了許多不同的lambda表達式。你將與​​RegisterCallback(Action<T> callback)碰上的一個主要問題 - 如果有人使用這樣的:

instance.RegisterCallback<string>(async _ => await Task.Delay(0)); 

他們正在創造一個async void,這是一個巨大的問題。您對Func<T, Task>有正確的想法。此外,我喜歡你的想法重新命名的功能,並添加「異步」似乎是合適的,RegisterAsyncCallback(Func<string, Task> callback)

+0

這是現貨 - 我忘記了'async void'可以投射到'Action '。據我所知,有沒有辦法編寫一個方法,明確禁止異步/非異步委託參數 – Rob

+0

我不知道什麼想知道實現?這只是一個回調(價值)。我需要在異步超載中處理更復雜的錯誤。 –

+0

用更多的代碼更新你的小提琴,我會再看一次。我看到的問題是'Action '總是會接受'RegisterCallback (async _ =>等待Task.Delay(0));'。所以沒有辦法區分。 –