實作事件架構非同步模式
更新:2007 年 11 月
如果您在撰寫的類別具有一些可能造成顯著延遲的作業,請參閱事件架構非同步模式概觀並考慮給予該類別非同步的功能。
對於具有非同步功能的類別,「事件架構非同步模式」提供了標準化的封裝 (Package) 方法。如果運用像是 AsyncOperationManager 的 Helper 類別來實作,您的類別就會在任何應用程式模型 (包括 ASP.NET、主控台應用程式 (Console Application) 及 Windows Form 應用程式) 之下正確地運作。
如需實作「事件架構非同步模式」的範例,請參閱 HOW TO:實作支援事件架構非同步模式的元件。
對於簡單的非同步作業 (Asynchronous Operation),BackgroundWorker 元件可能就相當適用。如需 BackgroundWorker 的詳細資訊,請參閱 HOW TO:在背景執行作業。
下列清單會描述本主題中討論的「事件架構非同步模式」的功能。
實作事件架構非同步模式的時機
命名非同步方法
選擇性地支援取消
選擇性地支援 IsBusy 屬性
選擇性地提供進度報告的支援
選擇性地提供傳回累加結果的支援
處理方法中的 Out 和 Ref 參數
實作事件架構非同步模式的時機
請考慮在以下時機實作「事件架構非同步模式」:
類別的用戶端不需要 WaitHandle 和 IAsyncResult 物件就可進行非同步作業,這表示用戶端需要建置輪詢和 WaitAll 或 WaitAny。
您要讓非同步作業由具有熟悉之事件/委派模型的用戶端來管理。
任何作業都是非同步實作的候選對象,不過對於預期會產生長久延遲的那些作業,就應該要多加考量。最適合的作業就是用戶端會在其中呼叫方法,並在完成時收到通知,而不需要進一步介入的作業。此外,持續執行並定期對用戶端通知進度、累加結果或狀態變更的作業,也相當適合。
如需決定何時支援「事件架構非同步模式」的詳細資訊,請參閱決定何時實作事件架構非同步模式。
命名非同步方法
對於您要為其提供非同步對應方法的每一個同步方法 MethodName:
定義 MethodNameAsync 方法:
傳回 void。
使用與 MethodName 方法相同的參數。
接受多個引動過程。
選擇性地定義與 MethodNameAsync 相同的 MethodNameAsync 多載,但是具有名稱為 userState 的額外物件值參數。如果是在準備管理方法的多個並行引動過程,就請如此進行。在此情況下,userState 值將會傳遞回到所有的事件處理常式,以區分方法的引動過程。您也可以選擇將此單純做為儲存使用者狀態的地方,以便能在以後擷取這些狀態。
對於個別的 MethodNameAsync 方法簽章:
在相同類別中將下列事件定義為方法:
Public Event MethodNameCompleted As MethodNameCompletedEventHandler
public event MethodNameCompletedEventHandler MethodNameCompleted;
定義下列委派和 AsyncCompletedEventArgs。這些委派最可能會在類別本身之外的相同命名空間中定義。
Public Delegate Sub MethodNameCompletedEventHandler( _ ByVal sender As Object, _ ByVal e As MethodNameCompletedEventArgs) Public Class MethodNameCompletedEventArgs Inherits System.ComponentModel.AsyncCompletedEventArgs Public ReadOnly Property Result() As MyReturnType End Property
public delegate void MethodNameCompletedEventHandler(object sender, MethodNameCompletedEventArgs e); public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { public MyReturnType Result { get; } }
確定 MethodNameCompletedEventArgs 類別將成員公開為唯讀屬性,而不是欄位,因為欄位會防止資料繫結。
請勿定義不會產生結果之方法的任何 AsyncCompletedEventArgs 衍生類別。請直接使用 AsyncCompletedEventArgs 本身的執行個體。
注意事項: 在可行和適當的狀況下,絕對可以接受重新使用委派和 AsyncCompletedEventArgs 型別。在此情況下,由於指定的委派和 AsyncCompletedEventArgs 不會繫結至單一方法,所以命名將不會與方法名稱一致。
選擇性地支援取消
如果您的類別支援取消非同步作業,就應該如以下所描述的,將取消公開到用戶端。請注意,在定義您的取消支援以前,必須先達成兩項決定:
您的類別,包含預期會在未來加入的項目,是否只有一個非同步作業支援取消?
支援取消的非同步作業,是否能支援多個擱置中的作業?也就是說,MethodNameAsync 方法是否使用 userState 參數,而且是否在等候任何作業完成之前允許多個引動過程?
請在下表中使用這兩個問題的答案,來決定您的取消方法應該具有的簽章。
Visual Basic
|
支援多個同步作業 |
一次只有一項作業 |
---|---|---|
整個類別只有一個非同步作業 |
|
|
在類別中具有多個非同步作業 |
|
|
C#
|
支援多個同步作業 |
一次只有一項作業 |
---|---|---|
整個類別只有一個非同步作業 |
|
|
在類別中具有多個非同步作業 |
|
|
如果定義了 CancelAsync(object userState) 方法,用戶端就必須謹慎選擇其狀態值,以便能夠區分在物件上叫用 (Invoke) 的所有非同步方法,而不只是單一非同步方法的所有引動過程。
決定命名單一非同步作業版本 MethodNameAsyncCancel,是基於能在像是 Visual Studio's IntelliSense 的設計環境中,更容易地探索到這項方法。此決定會將相關的成員編成群組,並將這些成員和其他與非同步功能無關的成員區隔。如果預期會在後續版本中加入額外的非同步作業,最好要定義 CancelAsync。
請勿在相同的類別中,定義多種以上表格中的方法。這樣做並無意義,而且將會使類別介面因為過多的方法而凌亂不堪。
這些方法通常都會立即傳回,而且作業不一定會確實取消。在 MethodNameCompleted 事件的事件處理常式中,MethodNameCompletedEventArgs 物件含有一個 Cancelled 欄位,可讓用戶端用來判斷是否已取消。
請遵守實作事件架構非同步模式的最佳作法中所描述的取消語意。
選擇性地支援 IsBusy 屬性
如果您的類別不支援多個並行引動過程,請考慮公開一個 IsBusy 屬性。如此讓開發人員不需攔截 MethodNameAsync 方法中的例外狀況,就可以判斷是否有 MethodNameAsync 方法正在執行。
請遵守實作事件架構非同步模式的最佳作法中所描述的 IsBusy 語意。
選擇性地提供進度報告的支援
許多開發人員經常希望在非同步作業進行時,它也能夠報告作業進度。「事件架構非同步模式」提供了一套方針,可讓您做到這點。
選擇性地定義由非同步作業所引發、並會在適當執行緒上叫用的事件。ProgressChangedEventArgs 物件附有整數值進度指示器 (Indicator),此指示器具有 0 和 100 之間的預期數值。
請如下所示名命這個事件:
如果類別具有多個非同步作業 (或是預期在未來的版本中將會增長,並包含多個非同步作業),則為 ProgressChanged;
MethodName 如果類別具有單一非同步作業,則為 ProgressChanged。
這項命名選擇與取消方法的命名類似 (如同「選擇性地支援取消」一節所描述的)。
這個事件應該使用 ProgressChangedEventHandler 委派簽章和 ProgressChangedEventArgs 類別。或者,如果可以提供更為定義域專屬的進度指示器 (例如,下載作業的讀取位元組和全部位元組),您就應該定義 ProgressChangedEventArgs 的衍生類別。
請注意,不論一個類別支援多少非同步方法,每個類別都只有一個 ProgressChanged 或 MethodNameProgressChanged 事件。用戶端必須使用傳遞至 MethodNameAsync 方法之 userState 物件,以區分多個並行作業上的進度更新。
也有可能出現多個作業都支援進度,卻都傳回不同的進度指示器的情況。在此情況下,單一的 ProgressChanged 事件並不適合,您應該考慮支援多個 ProgressChanged 事件。在這個情況下,使用每一個 MethodNameAsync 方法的 MethodNameProgressChanged 命名模式。
請遵守實作事件架構非同步模式的最佳作法中所描述的進度報告語意。
選擇性地提供傳回累加結果的支援
有時候非同步作業可能會在完成之前傳回累加結果。許多選項都能用來支援這種情況。以下提供一些範例。
單一作業類別
如果您的類別只支援單一非同步作業,而且該作業能夠傳回累加結果,就請:
擴充 ProgressChangedEventArgs 型別以附帶累加結果資料,並以這項擴充資料來定義 MethodNameProgressChanged 事件。
在出現可報告的累加結果時,引發這個 MethodNameProgressChanged 事件。
這項方案特別適用於單一非同步作業類別,因為如 MethodNameProgressChanged 事件所做的,相同事件在「所有作業」上發生並傳回累加結果時,並不會形成問題。
具有同質累加結果的多重作業類別
在此情況下,您的類別支援多個非同步方法,每個都能傳回累加結果,而且這些累加結果都擁有相同的資料型別。
請依照以上針對單一作業類別所描述的模型,因為同樣的 EventArgs 結構可以適用於所有的累加結果。定義 ProgressChanged 事件而非 MethodNameProgressChanged 事件,因為它會套用到多個非同步方法。
具有異質累加結果的多重作業類別
如果您的類別支援多個非同步方法,而每個方法都會傳回不同的資料型別,您就應該:
分隔您的累加結果報告和進度報告。
對於每個非同步方法,以適當的 EventArgs 定義個別的 MethodNameProgressChanged 事件,以處理該方法的累加結果資料。
如同實作事件架構非同步模式的最佳作法所描述的,在適當的執行緒上叫用該事件處理常式。
處理方法中的 Out 和 Ref 參數
一般而言,雖然不鼓勵在 .NET Framework 中使用 out 和 ref,但如果這些參數出現時,以下是必須遵循的規則:
假設有一個非同步方法 MethodName:
MethodName 的 out 參數不應該是 MethodNameAsync 的一部分。這些參數應該是 MethodNameCompletedEventArgs 的一部分,其名稱會與 MethodName 中的對等參數相同 (除非還有更適當的名稱)。
MethodName 的 ref 參數應該為 MethodNameAsync 的一部分,並且也是 MethodNameCompletedEventArgs 的一部分,其名稱會與 MethodName 中的對等參數相同 (除非還有更適當的名稱)。
例如,指定下列程式碼:
Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);
非同步方法及它的 AsyncCompletedEventArgs 類別應該如下所示:
Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)
Public Class MethodNameCompletedEventArgs
Inherits System.ComponentModel.AsyncCompletedEventArgs
Public ReadOnly Property Result() As Integer
End Property
Public ReadOnly Property Arg2() As String
End Property
Public ReadOnly Property Arg3() As String
End Property
End Class
public void MethodNameAsync(string arg1, string arg2);
public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
public int Result { get; };
public string Arg2 { get; };
public string Arg3 { get; };
}