2008-10-09 66 views
4

當編寫使用的BeginInvoke/EndInvoke會模式異步方法實現的代碼可能看起來像以下(和拯救你猜測這是圍繞一個緩存異步包裝):結束與部分類型信息異步委託調用

IAsyncResult BeginPut(string key, object value) 
{ 
    Action<string, object> put = this.cache.Put; 
    return put.BeginInvoke(key, value, null, null); 
} 

void EndPut(IAsyncResult asyncResult) 
{ 
    var put = (Action<string, object>)((AsyncResult)asyncResult).AsyncDelegate; 
    put.EndInvoke(asyncResult); 
} 

這很好,因爲它知道委託類型是什麼,所以可以投射。然而,當你有兩個Put方法時,它開始變得混亂,因爲儘管該方法返回void,但你似乎必須將其強制轉換爲強類型委託以結束調用,例如,

IAsyncResult BeginPut(string key, object value) 
{ 
    Action<string, object> put = this.cache.Put; 
    return put.BeginInvoke(key, value, null, null); 
} 

IAsyncResult BeginPut(string region, string key, object value) 
{ 
    Action<string, string, object> put = this.cache.Put; 
    return put.BeginInvoke(region, key, value, null, null); 
} 

void EndPut(IAsyncResult asyncResult) 
{ 
    var put = ((AsyncResult)asyncResult).AsyncDelegate; 

    var put1 = put as Action<string, object>; 
    if (put1 != null) 
    { 
     put1.EndInvoke(asyncResult); 
     return; 
    } 

    var put2 = put as Action<string, string, object>; 
    if (put2 != null) 
    { 
     put2.EndInvoke(asyncResult); 
     return; 
    } 

    throw new ArgumentException("Invalid async result", "asyncResult"); 
} 

我希望有做這樣一個乾淨的方式,因爲我在乎委託的唯一事情就是返回類型(在這種情況下無效),而不是那個被提供給它的參數。但是我絞盡腦汁問辦公室裏的其他人,沒有人能想到答案。

我知道有一個解決方法是編寫自定義IAsyncResult,但是這與周圍的事物,如WaitHandle的延遲實例,我寧願有這種略帶哈克看代碼的潛在的線程問題,這樣一個艱鉅的任務,不是走這條路線。

有關如何結束調用而沒有層疊集is檢查的任何想法?

回答

4

我錯了,有一個更清潔的方法。

您在相同的上下文中爲特定的EndInvoke()方法創建Action(IAsyncResult)代表,您已經知道委託的具體類型,並將其作爲AsyncState傳遞。爲了方便起見,我傳遞EndPut()作爲回調。

IAsyncResult BeginPut(string key, object value) { 
    Action<string, object> put = this.Put; 
    return put.BeginInvoke(key, value, EndPut, 
     new Action<IAsyncResult>(put.EndInvoke)); 
} 

IAsyncResult BeginPut(string region, string key, object value) { 
    Action<string, string, object> put = this.Put; 
    return put.BeginInvoke(region, key, value, EndPut, 
     new Action<IAsyncResult>(put.EndInvoke)); 
} 

然後你完成它。

void EndPut(IAsyncResult asyncResult) { 
    var del = asyncResult.AsyncState as Action<IAsyncResult>; 
    del(asyncResult); 
} 
0

爲什麼不只是要回更一般的過載避免這個問題:

IAsyncResult BeginPut(string key, object value) { 
    return this.BeginPut(null, key, value); 
} 

IAsyncResult BeginPut(string region, string key, object value) { 
    Action<string, string, object> put = this.Put; 
    return put.BeginInvoke(region, key, value, null, null); 
} 

void EndPut(IAsyncResult asyncResult) { 
    var put = (Action<string, string, object>)((AsyncResult)asyncResult).AsyncDelegate; 
    put.EndInvoke(asyncResult); 
} 
+0

不幸的是,認沽(字符串,對象)方法不通過認沽(字符串,字符串,對象)方法在內部,並且它是非法的傳遞null作爲第一個參數後者[這似乎因爲儘管名稱相似,它們有些不同的語義]。 – 2008-10-09 21:29:31

0

編輯:請看看我其他的答案。它可以更清潔。

我不認爲有辦法讓這個更清潔。實際上,通過使用相同的EndPut方法來結束多種類型的異步調用,實質上是不可避免的。通過將EndPutput代表作爲BeginInvoke()(回調和異步狀態)的最後兩個參數,並且您仍然必須測試代理的類型,以便調用EndInvoke(),您也可以遵循正常模式。

將它們投射到Delegate根本沒有幫助。

我喜歡Mark Brackett的想法。我認爲它歸結爲這些選項:

  • 加入他們完全通過一個重載呼叫另一個。一個代表,一個回調。
  • 通過撥打EndInvoke()有兩個回調完全分開它們。
從這些

不談,唯一要做的就是讓你的代碼只是一個小更清潔,使用使用asyncResult.AsyncState.GetType()一個switch或查找字典,傳遞put代表作爲狀態對象。

+0

不幸的是,對於Mark的回答評論,null對Put(string,string,object)的第一個參數是非法的,因此重載調用是不可能的。分離回調是不可能的,因爲EndPut需要被客戶端調用,並且必須按照慣例調用它。 – 2008-10-09 21:41:21

+0

所以這聽起來好像沒有什麼可以做的:(我真的很喜歡的是能夠將它轉換爲Action,因爲我不關心參數類型,但它似乎不是這種類型的在C#中,事情是可能的行動不是一個行動 – 2008-10-09 21:42:30