Share via


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

一部の操作によって著しい遅延が発生する可能性があるクラスを記述する場合は、イベントベースの非同期パターンの概要 の実装による非同期機能の追加を検討してください。

イベント ベースの非同期パターンは、非同期機能を持つクラスをパッケージ化するための、標準化された方法を提供します。 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 は単一のメソッドに結び付けられないため、名前付けはメソッド名と一致しません。

キャンセルのサポート (オプション)

クラスが非同期操作のキャンセルをサポートする場合、次のようにキャンセルをクライアントに公開する必要があります。 ただし、次の 2 点を決定してから、キャンセルのサポートを定義する必要があります。

  • クラスにキャンセルをサポートする非同期操作を 1 つだけ含めますか (今後予期される追加を含む)。

  • キャンセルをサポートする非同期操作で、複数の操作をサポートしますか。 つまり、MethodNameAsync メソッドで userState パラメーターを受け取り、他の完了を待たずに複数の呼び出しを許可しますか。

次の表に示すように、これらの 2 つの質問に対する回答を使用して、キャンセル メソッドのシグネチャを決定します。

Visual Basic

 

複数の同時操作をサポート

一度に 1 操作のみ

クラス全体で単一の非同期操作

Sub MethodNameAsyncCancel(ByVal userState As Object)
Sub MethodNameAsyncCancel()

クラス内で複数の非同期操作

Sub CancelAsync(ByVal userState As Object)
Sub CancelAsync()

C#

 

複数の同時操作をサポート

一度に 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。

    • クラスに単一の非同期操作がある場合は、MethodNameProgressChanged。

    この名前の付け方は、「キャンセルのサポート (オプション)」セクションで説明したキャンセル メソッドの場合と似ています。

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

サポートする非同期メソッドの数にかかわらず、ProgressChanged イベントまたは MethodNameProgressChanged イベントはクラスに 1 つだけである点に注意してください。 クライアントは、MethodNameAsync メソッドに渡される userState オブジェクトを使用して、複数の同時操作における進行状況の更新を識別します。

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

イベントベースの非同期パターンを実装するための推奨される手順」で説明した進行状況レポートのセマンティクスに従います。

インクリメンタル結果の提供のサポート (オプション)

非同期操作が、完了前にインクリメンタル結果を返す場合があります。 このシナリオのサポートに使用できるオプションがいくつかあります。 次にいくつかの例を示します。

単一操作クラス

クラスが単一の非同期操作のみをサポートし、その操作がインクリメンタル結果を返すことができる場合は、次の操作を行います。

  • インクリメンタル結果データを格納するように ProgressChangedEventArgs 型を拡張し、この拡張データを使用して MethodNameProgressChanged イベントを定義します。

  • レポートするインクリメンタル結果がある場合に、この MethodNameProgressChanged イベントを発生させます。

このソリューションは、特に単一の非同期操作に適用されます。この場合、MethodNameProgressChanged イベントのように、同じイベントが発生して "すべての操作" についてインクリメンタル結果を返しても問題がないからです。

同じインクリメンタル結果を持つ複数操作クラス

この場合、クラスは複数の非同期メソッドをサポートします。それぞれインクリメンタル結果を返すことができ、これらのインクリメンタル結果にはすべて、同じ型のデータが含まれます。

単一操作クラスの場合も、上記のモデルに従います。同じ EventArgs 構造体が、すべてのインクリメンタル結果で動作するからです。 MethodNameProgressChanged イベントの代わりに、複数の非同期メソッドに適用される ProgressChanged イベントを定義します。

異なるインクリメンタル結果を持つ複数操作クラス

クラスが複数の非同期メソッドをサポートし、それぞれが異なる型のデータを返す場合は、次の操作を行います。

  • インクリメンタル結果のレポートを進行状況レポートと分けます。

  • 非同期メソッドごとに、個別の MethodNameProgressChanged イベントを適切な EventArgs と共に定義して、そのメソッドのインクリメンタル結果データを処理します。

イベントベースの非同期パターンを実装するための推奨される手順」に説明されているように、そのイベント ハンドラーを適切なスレッドで呼び出します。

メソッド内の 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; };
}

参照

処理手順

方法 : イベントベースの非同期パターンをサポートするコンポーネントを実装する

方法 : バックグラウンドで操作を実行する

方法 : バックグラウンド操作を使用するフォームを実装する

参照

ProgressChangedEventArgs

AsyncCompletedEventArgs

概念

イベントベースの非同期パターンをいつ実装するかの決定

イベントベースの非同期パターンを実装するための推奨される手順

その他の技術情報

イベント ベースの非同期パターンを使用したマルチスレッド プログラミング