イベントベースの非同期パターンの実装Implementing the Event-based Asynchronous Pattern

顕著な遅延が発生する可能性がある操作を伴うクラスを作成する場合は、イベント ベースの非同期パターンを実装することによって、非同期機能を与えることを検討します。If you are writing a class with some operations that may incur noticeable delays, consider giving it asynchronous functionality by implementing Event-based Asynchronous Pattern Overview.

イベント ベースの非同期パターンは、非同期機能を持つクラスをパッケージ化するための標準的な方法を提供します。The Event-based Asynchronous Pattern provides a standardized way to package a class that has asynchronous features. AsyncOperationManager などのヘルパー クラスで実装しても、クラスは、ASP.NET、コンソール アプリケーション、Windows フォーム アプリケーションを含むすべてのアプリケーション モデルで正常に動作します。If implemented with helper classes like AsyncOperationManager, your class will work correctly under any application model, including ASP.NET, Console applications, and Windows Forms applications.

イベント ベースの非同期パターンを実装する例については、「方法:イベントベースの非同期パターンをサポートするコンポーネントを実装する」を参照してください。For an example that implements the Event-based Asynchronous Pattern, see How to: Implement a Component That Supports the Event-based Asynchronous Pattern.

単純な非同期操作の場合は、BackgroundWorker コンポーネントが適切である可能性があります。For simple asynchronous operations, you may find the BackgroundWorker component suitable. BackgroundWorker の詳細については、「方法:バックグラウンドで操作を実行する」を参照してください。For more information about BackgroundWorker, see How to: Run an Operation in the Background.

次の一覧は、このトピックで説明するイベント ベースの非同期パターンの機能を示しています。The following list describes the features of the Event-based Asynchronous Pattern discussed in this topic.

  • イベントベースの非同期パターンを実装するチャンスOpportunities for Implementing the Event-based Asynchronous Pattern

  • 非同期メソッドの名前付けNaming Asynchronous Methods

  • キャンセル処理の任意のサポートOptionally Support Cancellation

  • IsBusy プロパティの任意のサポートOptionally Support the IsBusy Property

  • 進行状況レポートの任意のサポートOptionally Provide Support for Progress Reporting

  • 増分の結果を返すことの任意のサポートOptionally Provide Support for Returning Incremental Results

  • メソッドでの Out パラメーターと Ref パラメーターの処理Handling Out and Ref Parameters in Methods

イベントベースの非同期パターンを実装するチャンスOpportunities for Implementing the Event-based Asynchronous Pattern

以下に該当する場合は、イベントベースの非同期パターンの実装を検討します。Consider implementing the Event-based Asynchronous Pattern when:

  • クラスのクライアントには、非同期操作に使用できる WaitHandleIAsyncResult オブジェクトは必要ありません。つまり、ポーリング、WaitAll または WaitAny は、クライアントによってビルドされる必要があるということです。Clients of your class do not need WaitHandle and IAsyncResult objects available for asynchronous operations, meaning that polling and WaitAll or WaitAny will need to be built up by the client.

  • クライアントでの非同期操作を、使い慣れたイベント/デリゲート モデルを使用して管理したい。You want asynchronous operations to be managed by the client with the familiar event/delegate model.

どの操作も非同期を実装するための候補になりますが、長い待機時間が予想される操作を考慮してください。Any operation is a candidate for an asynchronous implementation, but those you expect to incur long latencies should be considered. 特に適切なのは、クライアントがメソッドを呼び出し、完了時に通知を受け取ること以外の介入を必要としない操作です。Especially appropriate are operations in which clients call a method and are notified on completion, with no further intervention required. 継続的に実行され、進捗状況、増分結果、または状態の変更をクライアントに定期的に通知する操作も適しています。Also appropriate are operations which run continuously, periodically notifying clients of progress, incremental results, or state changes.

イベント ベースの非同期パターンをいつサポートするかを決定することの詳細については、「イベントベースの非同期パターンの実装時期を決定する」 (イベントベースの非同期パターンをいつ実装するかの決定) を参照してください。For more information on deciding when to support the Event-based Asynchronous Pattern, see Deciding When to Implement the Event-based Asynchronous Pattern.

非同期メソッドの名前付けNaming Asynchronous Methods

対応する非同期メソッドを用意する同期メソッド MethodName に対して、次の操作を行います。For each synchronous method MethodName for which you want to provide an asynchronous counterpart:

以下を実行する MethodNameAsync メソッドを定義します。Define a MethodNameAsync method that:

  • void を返します。Returns void.

  • MethodName メソッドと同じパラメーターを受け取ります。Takes the same parameters as the MethodName method.

  • 複数の呼び出しを受け入れます。Accepts multiple invocations.

必要に応じて、MethodNameAsync と同一の MethodNameAsync オーバーロードを定義しますが、userState という追加のオブジェクト値パラメーターを使用して定義します。Optionally define a MethodNameAsync overload, identical to MethodNameAsync, but with an additional object-valued parameter called userState. これは、メソッドの複数の同時呼び出しを管理するために実行します。この場合、メソッドの呼び出しを区別するために userState 値がすべてのイベント ハンドラーに返されます。Do this if you're prepared to manage multiple concurrent invocations of your method, in which case the userState value will be delivered back to all event handlers to distinguish invocations of the method. 単純に後で取得するためにユーザーの状態を格納する場所として実行することもできます。You may also choose to do this simply as a place to store user state for later retrieval.

個別の MethodNameAsync メソッドのシグネチャに対して、次の手順を実行します。For each separate MethodNameAsync method signature:

  1. メソッドと同じクラス内に次のイベントを定義します。Define the following event in the same class as the method:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. 次のデリゲートと AsyncCompletedEventArgs を定義します。Define the following delegate and AsyncCompletedEventArgs. これらは、クラス自体の外部に定義される可能性がありますが、同じ名前空間に定義されます。These will likely be defined outside of the class itself, but in the same namespace.

    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 クラスで、フィールドではなくメンバーを読み取り専用プロパティとして公開していることを確認します。これは、フィールドはデータ バインドを妨げるためです。Ensure that the MethodNameCompletedEventArgs class exposes its members as read-only properties, and not fields, as fields prevent data binding.

    • 結果を生成しないメソッドに AsyncCompletedEventArgs の派生クラスを定義しないでください。Do not define any AsyncCompletedEventArgs-derived classes for methods that do not produce results. 単純に AsyncCompletedEventArgs のインスタンス自体を使用します。Simply use an instance of AsyncCompletedEventArgs itself.

      注意

      実行可能かつ適切な場合、デリゲートと AsyncCompletedEventArgs 型を再利用することはまったく問題ありません。It is perfectly acceptable, when feasible and appropriate, to reuse delegate and AsyncCompletedEventArgs types. この場合、特定のデリゲートと AsyncCompletedEventArgs が 1 つのメソッドに関連付けられることがないため、メソッド名のような名前の一貫性はなくなります。In this case, the naming will not be as consistent with the method name, since a given delegate and AsyncCompletedEventArgs won't be tied to a single method.

キャンセル処理の任意のサポートOptionally Support Cancellation

クラスで非同期操作のキャンセルをサポートする場合は、以下に示すようにキャンセルをクライアントに公開する必要があります。If your class will support canceling asynchronous operations, cancellation should be exposed to the client as described below. キャンセルのサポートを定義する前に、以下の 2 点について判断する必要があることに注意してください。Note that there are two decision points that need to be reached before defining your cancellation support:

  • 今後の予想される追加機能を含め、クラスには、キャンセルをサポートする非同期操作が 1 つだけあるかどうか。Does your class, including future anticipated additions to it, have only one asynchronous operation that supports cancellation?

  • キャンセルをサポートする非同期操作が、複数の保留中の操作をサポートできるかどうか。Can the asynchronous operations that support cancellation support multiple pending operations? これは、MethodNameAsync メソッドが userState パラメーターを受け取り、いずれかの呼び出しが終了するまで待機する前に、複数の呼び出しを許可できるかどうかを意味します。That is, does the MethodNameAsync method take a userState parameter, and does it allow multiple invocations before waiting for any to finish?

これら 2 つの質問に対する回答を次の表に当てはめて、キャンセル メソッドで使用するシグネチャを決定します。Use the answers to these two questions in the table below to determine what the signature for your cancellation method should be.

Visual BasicVisual Basic

複数の同時操作をサポートMultiple Simultaneous Operations Supported 一度に 1 つだけの操作Only One Operation at a Time
クラス全体で 1 つの非同期操作One Async Operation in entire class Sub MethodNameAsyncCancel(ByVal userState As Object) Sub MethodNameAsyncCancel()
クラスの複数の非同期操作Multiple Async Operations in class Sub CancelAsync(ByVal userState As Object) Sub CancelAsync()

C#C#

複数の同時操作をサポートMultiple Simultaneous Operations Supported 一度に 1 つだけの操作Only One Operation at a Time
クラス全体で 1 つの非同期操作One Async Operation in entire class void MethodNameAsyncCancel(object userState); void MethodNameAsyncCancel();
クラスの複数の非同期操作Multiple Async Operations in class void CancelAsync(object userState); void CancelAsync();

CancelAsync(object userState) メソッドを定義する場合、クライアントは、1 つの非同期メソッドのすべての呼び出しの間だけではなく、オブジェクトに対して呼び出されたすべての非同期メソッドの間で区別できるようにする状態値を慎重に選択する必要があります。If you define the CancelAsync(object userState) method, clients must be careful when choosing their state values to make them capable of distinguishing among all asynchronous methods invoked on the object, and not just between all invocations of a single asynchronous method.

1 つの非同期操作のバージョンの MethodNameAsyncCancel という名前は、Visual Studio の IntelliSense のようなデザイン環境でそのメソッドをより簡単に検出できるようにするために、このように決定されています。The decision to name the single-async-operation version MethodNameAsyncCancel is based on being able to more easily discover the method in a design environment like Visual Studio's IntelliSense. これにより、関連するメンバーがグループ化され、非同期機能とは無関係の他のメンバーとは区別されます。This groups the related members and distinguishes them from other members that have nothing to do with asynchronous functionality. 今後のバージョンで非同期操作を追加する可能性がある場合は、CancelAsync を定義することをお勧めします。If you expect that there may be additional asynchronous operations added in subsequent versions, it is better to define CancelAsync.

同じクラスに上の表の複数のメソッドを定義しないでください。Do not define multiple methods from the table above in the same class. それは意味のない行為であるか、メソッドの増加によってクラス インターフェイスがわかりにくくなります。That will not make sense, or it will clutter the class interface with a proliferation of methods.

これらのメソッドは、通常は直ちに結果が戻り、操作が実際にキャンセルされる場合もキャンセルされない場合もあります。These methods typically will return immediately, and the operation may or may not actually cancel. MethodNameCompleted イベント用のイベント ハンドラーでは、MethodNameCompletedEventArgs オブジェクトに Cancelled フィールドが含まれます。クライアントはこれを使用して、キャンセルが発生したかどうかを判断できます。In the event handler for the MethodNameCompleted event, the MethodNameCompletedEventArgs object contains a Cancelled field, which clients can use to determine whether the cancellation occurred.

イベントベースの非同期パターンを実装するための推奨される手順」に説明されているキャンセルのセマンティクスに従ってください。Abide by the cancellation semantics described in Best Practices for Implementing the Event-based Asynchronous Pattern.

IsBusy プロパティの任意のサポートOptionally Support the IsBusy Property

クラスが複数の同時呼び出しをサポートしない場合は、IsBusy プロパティを公開することを検討してください。If your class does not support multiple concurrent invocations, consider exposing an IsBusy property. 開発者は、このプロパティによって、MethodNameAsync メソッドが MethodNameAsync メソッドからの例外をキャッチせずに実行されているかどうかを判断できます。This allows developers to determine whether a MethodNameAsync method is running without catching an exception from the MethodNameAsync method.

イベントベースの非同期パターンを実装するための推奨される手順」に説明されている IsBusy のセマンティクスに従ってください。Abide by the IsBusy semantics described in Best Practices for Implementing the Event-based Asynchronous Pattern.

進行状況レポートの任意のサポートOptionally Provide Support for Progress Reporting

非同期操作の操作中に進行状況を報告することをお勧めします。It is frequently desirable for an asynchronous operation to report progress during its operation. イベント ベースの非同期パターンには、これを行うためのガイドラインがあります。The Event-based Asynchronous Pattern provides a guideline for doing so.

  • 必要に応じて、非同期操作によって発生し、適切なスレッドで呼び出されるイベントを定義します。Optionally define an event to be raised by the asynchronous operation and invoked on the appropriate thread. ProgressChangedEventArgs オブジェクトは、0 ~ 100 の範囲であることが期待されている整数値の進行状況インジケーターを伝達します。The ProgressChangedEventArgs object carries an integer-valued progress indicator that is expected to be between 0 and 100.

  • このイベントには、次のように名前を付けます。Name this event as follows:

    • ProgressChanged: クラスに複数の非同期操作がある (または将来のバージョンで複数の非同期操作を含めることが予想される) 場合。ProgressChanged if the class has multiple asynchronous operations (or is expected to grow to include multiple asynchronous operations in future versions);

    • MethodNameProgressChanged クラスに非同期操作が 1 つだけある場合。MethodNameProgressChanged if the class has a single asynchronous operation.

    この名前の付け方は、「キャンセルの任意のサポート」セクションで説明した、キャンセル メソッドに対する方法と対応しています。This naming choice parallels that made for the cancellation method, as described in the Optionally Support Cancellation section.

このイベントは、ProgressChangedEventHandler デリゲート シグネチャと ProgressChangedEventArgs クラスを使用する必要があります。This event should use the ProgressChangedEventHandler delegate signature and the ProgressChangedEventArgs class. 別の方法として、ドメイン固有の進行状況インジケーター (読み取られたバイト数やダウンロード操作の合計バイト数など) を提供できる場合は、ProgressChangedEventArgs の派生クラスを定義します。Alternatively, if a more domain-specific progress indicator can be provided (for instance, bytes read and total bytes for a download operation), then you should define a derived class of ProgressChangedEventArgs.

サポートされる非同期メソッドの数にかかわらず、クラスの ProgressChanged または MethodNameProgressChanged イベントは 1 つしかないことに注意してください。Note that there is only one ProgressChanged or MethodNameProgressChanged event for the class, regardless of the number of asynchronous methods it supports. クライアントは、MethodNameAsync メソッドに渡される userState オブジェクトを使用して、複数の同時操作に対する進行状況の更新を区別することが期待されています。Clients are expected to use the userState object that is passed to the MethodNameAsync methods to distinguish among progress updates on multiple concurrent operations.

複数の操作で進行状況がサポートされ、それぞれが異なる進行状況インジケーターを返す状況が発生することがあります。There may be situations in which multiple operations support progress and each returns a different indicator for progress. この場合は、1 つの ProgressChanged イベントでは不適切であり、複数の ProgressChanged のサポートを検討することができます。In this case, a single ProgressChanged event is not appropriate, and you may consider supporting multiple ProgressChanged events. この場合は、各 MethodNameAsync メソッドに対して、MethodNameProgressChanged の名前付けパターンを使用します。In this case use a naming pattern of MethodNameProgressChanged for each MethodNameAsync method.

イベントベースの非同期パターンを実装するための推奨される手順」に説明されている進行状況報告のセマンティクスに従ってください。Abide by the progress-reporting semantics described Best Practices for Implementing the Event-based Asynchronous Pattern.

増分の結果を返すことの任意のサポートOptionally Provide Support for Returning Incremental Results

場合によっては、非同期操作が完了する前に、増分結果が返ることがあります。Sometimes an asynchronous operation can return incremental results prior to completion. このシナリオをサポートするために使用できるオプションは多数あります。There are a number of options that can be used to support this scenario. 以下に、いくつかの例を示します。Some examples follow.

単一操作クラスSingle-operation Class

クラスが 1 つの非同期操作のみをサポートし、その操作が増分結果を返すことができる場合は、次のようにします。If your class only supports a single asynchronous operation, and that operation is able to return incremental results, then:

  • ProgressChangedEventArgs 型を増分結果データを伝達するように拡張し、この拡張されたデータを使用する MethodNameProgressChanged イベントを定義します。Extend the ProgressChangedEventArgs type to carry the incremental result data, and define a MethodNameProgressChanged event with this extended data.

  • 報告する増分結果があるときに、この MethodNameProgressChanged イベントを発生させます。Raise this MethodNameProgressChanged event when there is an incremental result to report.

このソリューションは、MethodNameProgressChanged イベントと同じように、同じイベントの発生で "すべての操作" に対して増分結果を返しても何の問題もないため、特に単一非同期操作のクラスに適用されます。This solution applies specifically to a single-async-operation class because there is no problem with the same event occurring to return incremental results on "all operations", as the MethodNameProgressChanged event does.

同じ型の増分結果を持つ複数操作クラスMultiple-operation Class with Homogeneous Incremental Results

この場合、クラスは、それぞれが増分結果を返すことができる複数の非同期メソッドをサポートし、増分結果はすべて同じ型のデータを持っています。In this case, your class supports multiple asynchronous methods, each capable of returning incremental results, and these incremental results all have the same type of data.

同じ EventArgs 構造体がすべての増分結果で機能するため、上記の単一操作クラスで説明したモデルに従います。Follow the model described above for single-operation classes, as the same EventArgs structure will work for all incremental results. 複数の非同期メソッドに適用するため、MethodNameProgressChanged イベントの代わりに ProgressChanged イベントを定義します。Define a ProgressChanged event instead of a MethodNameProgressChanged event, since it applies to multiple asynchronous methods.

異なる型の増分結果を持つ複数操作クラスMultiple-operation Class with Heterogeneous Incremental Results

クラスが複数の非同期メソッドをサポートし、それぞれが異なる型のデータを返す場合は、以下を行います。If your class supports multiple asynchronous methods, each returning a different type of data, you should:

  • 増分結果の報告と進行状況の報告を分離します。Separate your incremental result reporting from your progress reporting.

  • 各非同期メソッドの増分結果データを処理する個別の MethodNameProgressChanged イベントを、適切な EventArgs で定義します。Define a separate MethodNameProgressChanged event with appropriate EventArgs for each asynchronous method to handle that method's incremental result data.

イベントベースの非同期パターンを実装するための推奨される手順」に説明されているように、そのイベント ハンドラーを適切なスレッドで呼び出します。Invoke that event handler on the appropriate thread as described in Best Practices for Implementing the Event-based Asynchronous Pattern.

メソッドでの Out パラメーターと Ref パラメーターの処理Handling Out and Ref Parameters in Methods

一般に、.NET Framework では、outref の使用は避けることを推奨していますが、それらが存在するときのルールを次に示します。Although the use of out and ref is, in general, discouraged in the .NET Framework, here are the rules to follow when they are present:

非同期メソッド MethodName の場合:Given a synchronous method MethodName:

  • MethodName に対する out パラメーターは、MethodNameAsync の一部にしないでください。out parameters to MethodName should not be part of MethodNameAsync. 代わりに、MethodName での同等のパラメーターと同じ名前の MethodNameCompletedEventArgs の一部にする必要があります (より適切な名前がない限り)。Instead, they should be part of MethodNameCompletedEventArgs with the same name as its parameter equivalent in MethodName (unless there is a more appropriate name).

  • MethodName に対する ref パラメーターは、MethodNameAsync の一部として出現し、MethodName での同等のパラメーターと同じ名前の MethodNameCompletedEventArgs の一部として出現する必要があります (より適切な名前がない限り)。ref parameters to MethodName should appear as part of MethodNameAsync, and as part of MethodNameCompletedEventArgs with the same name as its parameter equivalent in MethodName (unless there is a more appropriate name).

次に例を示します。For example, given:

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 クラスは、次のようになります。Your asynchronous method and its AsyncCompletedEventArgs class would look like this:

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; };
}

関連項目See also