次の方法で共有


イベント ベースの非同期パターンの実装

顕著な遅延が発生する可能性のあるいくつかの操作でクラスを記述する場合は、 イベント ベースの非同期パターンを実装して非同期機能を提供することを検討してください。

イベント ベースの非同期パターンは、非同期機能を持つクラスをパッケージ化する標準化された方法を提供します。 AsyncOperationManagerなどのヘルパー クラスを使用して実装すると、ASP.NET、コンソール アプリケーション、Windows フォーム アプリケーションなど、任意のアプリケーション モデルでクラスが正しく動作します。

イベント ベースの非同期パターンを実装する例については、「 方法: イベント ベースの非同期パターンをサポートするコンポーネントを実装する」を参照してください。

単純な非同期操作の場合、 BackgroundWorker コンポーネントが適している場合があります。 BackgroundWorkerの詳細については、「方法: バックグラウンドで操作を実行する」を参照してください。

次の一覧では、このトピックで説明するイベント ベースの非同期パターンの機能について説明します。

  • イベント ベースの非同期パターンを実装する機会

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

  • 必要に応じてキャンセルをサポートする

  • 必要に応じて IsBusy プロパティをサポートする

  • 必要に応じて、進行状況レポートのサポートを提供する

  • 増分の結果を返すことの任意のサポート

  • メソッドでの Out パラメーターと Ref パラメーターの処理

イベント ベースの非同期パターンを実装する機会

次の場合は、イベントベースの非同期パターンの実装を検討してください。

  • クラスのクライアントは、非同期操作に使用できる WaitHandle オブジェクトと IAsyncResult オブジェクトを必要としません。つまり、ポーリングと WaitAll または WaitAny をクライアントによって構築する必要があります。

  • 使い慣れたイベント/デリゲート モデルを使用して、クライアントによって非同期操作を管理する必要があります。

どの操作も非同期実装の候補ですが、待機時間が長いと予想される操作を考慮する必要があります。 特に適切なのは、クライアントがメソッドを呼び出し、完了時に通知を受け取る操作であり、それ以上の介入は必要ありません。 また、継続的に実行され、進行状況、増分結果、または状態の変更をクライアントに定期的に通知する操作も適切です。

イベント ベースの非同期パターンをサポートするタイミングの決定の詳細については、「イベント ベースの非同期パターン を実装するタイミングの決定」を参照してください。

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

非同期の対応メソッドを提供したい各同期メソッド MethodName に対して、次の手順を実行します。

次の MethodNameAsync メソッドを定義します。

  • void を返します。

  • MethodName メソッドと同じパラメーターを受け取ります。

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

必要に応じて、 MethodNameAsync と同じ MethodNameAsync オーバーロードを定義しますが、 userStateと呼ばれる追加のオブジェクト値パラメーターを使用します。 メソッドの複数の同時呼び出しを管理する準備ができている場合は、これを行います。その場合、 userState 値はすべてのイベント ハンドラーに返され、メソッドの呼び出しを区別します。 これは、後で取得するためにユーザーの状態を格納する場所として選択することもできます。

個別の MethodNameAsync メソッド シグネチャごとに、次の手順を実行します。

  1. メソッドと同じクラスで次のイベントを定義します。

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. 次のデリゲートと 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 は 1 つのメソッドに関連付けられていないため、名前付けはメソッド名と同じになりません。

必要に応じてキャンセルをサポートする

クラスで非同期操作の取り消しがサポートされる場合は、次の説明に従ってキャンセルをクライアントに公開する必要があります。 取り消しサポートを定義する前に、次の 2 つの決定ポイントに到達する必要があります。

  • 将来予想される追加を含むクラスには、キャンセルをサポートする非同期操作が 1 つだけ含まれていますか?
  • 取り消しをサポートする非同期操作は、複数の保留中の操作をサポートできますか? つまり、 MethodNameAsync メソッドは userState パラメーターを受け取り、完了を待機する前に複数の呼び出しを許可しますか?

次の表の 2 つの質問に対する回答を使用して、キャンセル方法のシグネチャを決定します。

Visual Basic

サポートされている複数の同時操作 一度に 1 つの操作のみ
クラス全体で 1 つの非同期操作 Sub MethodNameAsyncCancel(ByVal userState As Object) Sub MethodNameAsyncCancel()
クラス内の複数の非同期操作 Sub CancelAsync(ByVal userState As Object) Sub CancelAsync()

C#

サポートされている複数の同時操作 一度に 1 つの操作のみ
クラス全体で 1 つの非同期操作 void MethodNameAsyncCancel(object userState); void MethodNameAsyncCancel();
クラス内の複数の非同期操作 void CancelAsync(object userState); void CancelAsync();

CancelAsync(object userState) メソッドを定義する場合、クライアントは状態値を選択するときに注意して、単一の非同期メソッドのすべての呼び出し間だけでなく、オブジェクトで呼び出されたすべての非同期メソッドを区別できるようにする必要があります。

単一非同期操作バージョン MethodNameAsyncCancel に名前を付ける決定は、Visual Studio の IntelliSense などのデザイン環境でメソッドをより簡単に検出できることに基づいています。 これにより、関連するメンバーがグループ化され、非同期機能とは関係のない他のメンバーと区別されます。 後続のバージョンで追加の非同期操作が追加される可能性がある場合は、 CancelAsyncを定義することをお勧めします。

上の表の複数のメソッドを同じクラスで定義しないでください。 それは意味をなさないか、メソッドの急増でクラスインターフェイスを混乱させるでしょう。

これらのメソッドは、通常は直ちに結果が戻り、操作が実際にキャンセルされる場合もキャンセルされない場合もあります。 MethodNameCompleted イベントのイベント ハンドラーでは、MethodNameCompletedEventArgs オブジェクトに Cancelled フィールドが含まれています。このフィールドは、クライアントがキャンセルが発生したかどうかを判断するために使用できます。

イベント ベースの非同期パターンを実装するためのベスト プラクティスで説明されているキャンセル セマンティクスに従います。

必要に応じて IsBusy プロパティをサポートする

クラスが複数の同時呼び出しをサポートしていない場合は、 IsBusy プロパティを公開することを検討してください。 これにより、開発者は MethodNameAsync メソッドから例外をキャッチせずに MethodNameAsync メソッドが実行されているかどうかを判断できます。

IsBusyで説明されているセマンティクスに従います。

必要に応じて、進行状況レポートのサポートを提供する

非同期操作では、操作中に進行状況を報告することが望ましい場合が多くなります。 イベント ベースの非同期パターンには、これを行うためのガイドラインが用意されています。

  • 必要に応じて、非同期操作によって発生し、適切なスレッドで呼び出されるイベントを定義します。 ProgressChangedEventArgs オブジェクトには、0 ~ 100 の範囲で予想される整数値の進行状況インジケーターが含まれています。

  • このイベントに次の名前を付けます。

    • ProgressChanged クラスに複数の非同期操作がある場合 (または将来のバージョンで複数の非同期操作を含むように拡張することが予想される場合)。

    • クラスに 1 つの非同期操作がある場合、MethodNameProgressChanged を使用する。

    この名前付けの選択は、「オプションでキャンセルをサポートする」セクションの説明に従って、取り消し方法に対して行われたと同じになります。

このイベントでは、 ProgressChangedEventHandler デリゲート シグネチャと ProgressChangedEventArgs クラスを使用する必要があります。 または、ドメイン固有の進行状況インジケーターを提供できる場合 (ダウンロード操作の読み取りバイト数や合計バイト数など)、 ProgressChangedEventArgsの派生クラスを定義する必要があります。

サポートされている非同期メソッドの数に関係なく、クラスの ProgressChanged または MethodNameProgressChanged イベントは 1 つだけであることに注意してください。 クライアントは、userStateAsync メソッドに渡される オブジェクトを使用して、複数の同時実行操作の進行状況の更新を区別することが期待されます。

複数の操作が進行状況をサポートし、それぞれが進行状況の異なるインジケーターを返す場合があります。 この場合、1 つの ProgressChanged イベントは適切ではなく、複数の ProgressChanged イベントをサポートすることを検討してください。 この場合は、MethodName Async メソッドごとに MethodNameProgressChanged の名前付けパターン使用します。

イベント ベースの非同期パターンを実装するためのベスト プラクティスについて説明されている進行状況レポートセマンティクスに従います。

増分の結果を返すことの任意のサポート

非同期操作が完了前に増分結果を返すことがあります。 このシナリオをサポートするために使用できるオプションは多数あります。 いくつかの例が続きます。

単一操作クラス

クラスが 1 つの非同期操作のみをサポートし、その操作が増分結果を返すことができる場合は、次のようになります。

  • 増分結果データを伝達するように ProgressChangedEventArgs 型を拡張し、この拡張データを使用して MethodNameProgressChanged イベントを定義します。

  • レポートする増分結果がある場合は、この MethodNameProgressChanged イベントを発生させます。

このソリューションは、 MethodNameProgressChanged イベントと同様に、"すべての操作" で増分結果を返すのと同じイベントに問題がないため、単一非同期操作クラスに特に適用されます。

同種の増分結果を持つ複数操作クラス

この場合、クラスは複数の非同期メソッドをサポートし、それぞれが増分結果を返すことができます。これらの増分結果はすべて同じ種類のデータを持っています。

同じ EventArgs 構造がすべての増分結果に対して機能するように、単一操作クラスについては前述のモデルに従います。 複数の非同期メソッドに適用されるため、ProgressChangedProgressChanged イベントではなく、 イベントを定義します。

異種の増分結果を持つ複数操作クラス

クラスが複数の非同期メソッドをサポートし、それぞれが異なる種類のデータを返す場合は、次の手順を実行する必要があります。

  • 増分結果レポートと進行状況レポートを分離します。

  • 非同期メソッドごとに適切なを使用して個別の MethodNameEventArgs イベントを定義し、そのメソッドの増分結果データを処理します。

イベント ベースの非同期パターンを実装するためのベスト プラクティスの説明に従って、適切なスレッドでそのイベント ハンドラーを呼び出します。

メソッドでの Out パラメーターと Ref パラメーターの処理

outrefの使用は、一般的に .NET では推奨されませんが、存在する場合に従う規則を次に示します。

同期メソッド MethodName を指定します。

  • out MethodName へのパラメーターは、MethodNameAsync の一部にすることはできません。 MethodNameCompletedEventArgs は、MethodName における同じ名前のパラメーターに相当するものの一部である必要があります(適切な名前が他にない場合を除く)。

  • ref MethodName へのパラメーターは 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; };
}

こちらも参照ください