イベントベースの非同期パターンを実装するための推奨される手順Best Practices for Implementing the Event-based Asynchronous Pattern

イベントベースの非同期パターンは、使い慣れたイベントおよびデリゲートのセマンティクスと共に、クラス内の非同期動作を公開する効果的な方法を提供します。The Event-based Asynchronous Pattern provides you with an effective way to expose asynchronous behavior in classes, with familiar event and delegate semantics. イベント ベースの非同期パターンを実装するには、いくつかの固有の動作要件に従う必要があります。To implement Event-based Asynchronous Pattern, you need to follow some specific behavioral requirements. 以降のセクションでは、イベントベースの非同期パターンに従うクラスを実装する際に検討すべき要件とガイドラインについて説明します。The following sections describe requirements and guidelines you should consider when you implement a class that follows the Event-based Asynchronous Pattern.

概要については、「イベントベースの非同期パターンの実装」を参照してください。For an overview, see Implementing the Event-based Asynchronous Pattern.

必要な動作保証Required Behavioral Guarantees

イベントベースの非同期パターンを実装する場合は、クラスが適切に動作し、クラスのクライアントがそのような動作に依存できるようにするため、多数の保証を提供する必要があります。If you implement the Event-based Asynchronous Pattern, you must provide a number of guarantees to ensure that your class will behave properly and clients of your class can rely on such behavior.

完了Completion

正常完了、エラー、またはキャンセルの場合に常に MethodNameCompleted イベント ハンドラーを呼び出します。Always invoke the MethodNameCompleted event handler when you have successful completion, an error, or a cancellation. アプリケーションがアイドルになり完了しない状態が発生してはなりません。Applications should never encounter a situation where they remain idle and completion never occurs. この規則の唯一の例外として、非同期操作自体は完了することがないように設計されている場合があります。One exception to this rule is if the asynchronous operation itself is designed so that it never completes.

完了イベントおよび EventArgCompleted Event and EventArgs

個別の MethodNameAsync メソッドごとに、次の設計要件を適用します。For each separate MethodNameAsync method, apply the following design requirements:

  • メソッドと同じクラスで MethodNameCompleted イベントを定義します。Define a MethodNameCompleted event on the same class as the method.

  • AsyncCompletedEventArgs クラスから派生した MethodNameCompleted イベントの EventArgs クラスと、これに付随するデリゲートを定義します。Define an EventArgs class and accompanying delegate for the MethodNameCompleted event that derives from the AsyncCompletedEventArgs class. 既定のクラス名の形式は、MethodNameCompletedEventArgs です。The default class name should be of the form MethodNameCompletedEventArgs.

  • EventArgs クラスは、MethodName メソッドの戻り値に固有のクラスにしてください。Ensure that the EventArgs class is specific to the return values of the MethodName method. EventArgs クラスを使用する場合は、開発者に対して結果をキャストすることを義務付けないでください。When you use the EventArgs class, you should never require developers to cast the result.

    設計要件の適切な実装と不適切な実装を次のコード例に示します。The following code example shows good and bad implementation of this design requirement respectively.

// Good design  
private void Form1_MethodNameCompleted(object sender, xxxCompletedEventArgs e)   
{   
    DemoType result = e.Result;  
}  
  
// Bad design  
private void Form1_MethodNameCompleted(object sender, MethodNameCompletedEventArgs e)   
{   
    DemoType result = (DemoType)(e.Result);  
}  
  • EventArgs を返すメソッドのために void クラスを定義しないでください。Do not define an EventArgs class for returning methods that return void. 代わりに AsyncCompletedEventArgs クラスのインスタンスを使用してください。Instead, use an instance of the AsyncCompletedEventArgs class.

  • 必ず常に MethodNameCompleted イベントを発生させます。Ensure that you always raise the MethodNameCompleted event. このイベントは、正常完了、エラー、またはキャンセル時に発生する必要があります。This event should be raised on successful completion, on an error, or on cancellation. アプリケーションがアイドルになり完了しない状態が発生してはなりません。Applications should never encounter a situation where they remain idle and completion never occurs.

  • 非同期操作で発生した例外をすべてキャッチし、キャッチした例外を Error プロパティに割り当ててください。Ensure that you catch any exceptions that occur in the asynchronous operation and assign the caught exception to the Error property.

  • タスクの実行中にエラーが発生した場合は、結果にアクセスできないようにしてください。If there was an error completing the task, the results should not be accessible. Error プロパティが null ではない場合は、EventArgs 構造体のプロパティにアクセスすると例外が発生するようにしてください。When the Error property is not null, ensure that accessing any property in the EventArgs structure raises an exception. この検証を行うには、RaiseExceptionIfNecessary メソッドを使用します。Use the RaiseExceptionIfNecessary method to perform this verification.

  • タイムアウトをエラーとしてモデル化します。Model a time out as an error. タイムアウトが発生したら、MethodNameCompleted イベントを発生させ、TimeoutExceptionError プロパティに割り当てます。When a time out occurs, raise the MethodNameCompleted event and assign a TimeoutException to the Error property.

  • クラスで複数の同時呼び出しがサポートされている場合は、必ず MethodNameCompleted イベントに適切な userSuppliedState オブジェクトが含まれるようにします。If your class supports multiple concurrent invocations, ensure that the MethodNameCompleted event contains the appropriate userSuppliedState object.

  • MethodNameCompleted イベントが、適切なスレッドでアプリケーション サイクルの適切な時点で発生するようにしてください。Ensure that the MethodNameCompleted event is raised on the appropriate thread and at the appropriate time in the application lifecycle. 詳細については、「スレッド処理およびコンテキスト」を参照してください。For more information, see the Threading and Contexts section.

操作の同時実行Simultaneously Executing Operations

  • クラスで複数の同時呼び出しがサポートされている場合は、開発者が各呼び出しを個別に追跡できるようにするため、userSuppliedState というオブジェクト値状態パラメーター、またはタスク ID を受け取る MethodNameAsync オーバーライドを定義します。If your class supports multiple concurrent invocations, enable the developer to track each invocation separately by defining the MethodNameAsync overload that takes an object-valued state parameter, or task ID, called userSuppliedState. このパラメーターは、常に MethodNameAsync メソッドのシグネチャの最終パラメーターにする必要があります。This parameter should always be the last parameter in the MethodNameAsync method's signature.

  • オブジェクト値状態パラメーターまたはタスク ID を受け取る MethodNameAsync オーバーロードがクラスによって定義される場合は、そのタスク ID の操作の有効期間を追跡し、完了ハンドラーに戻す必要があります。If your class defines the MethodNameAsync overload that takes an object-valued state parameter, or task ID, be sure to track the lifetime of the operation with that task ID, and be sure to provide it back into the completion handler. 役に立つヘルパー クラスがあります。There are helper classes available to assist. コンカレンシー管理の詳細については、「方法: イベントベースの非同期パターンをサポートするコンポーネントを実装する」を参照してください。For more information on concurrency management, see How to: Implement a Component That Supports the Event-based Asynchronous Pattern.

  • クラスによって、状態パラメーターなしで MethodNameAsync メソッドが定義され、このクラスで複数の同時呼び出しがサポートされていない場合、直前の MethodNameAsync 呼び出しが完了する前に MethodNameAsync を呼び出そうとすると、InvalidOperationException が発生するようにします。If your class defines the MethodNameAsync method without the state parameter, and it does not support multiple concurrent invocations, ensure that any attempt to invoke MethodNameAsync before the prior MethodNameAsync invocation has completed raises an InvalidOperationException.

  • 一般に、userSuppliedState パラメーターのない MethodNameAsync メソッドを複数回呼び出すときには、複数の未完了操作が存在するようにするため、例外を発生させないでください。In general, do not raise an exception if the MethodNameAsync method without the userSuppliedState parameter is invoked multiple times so that there are multiple outstanding operations. 例外を発生させることができるのは、クラスがその状況に対処できないことが明らかであるものの、開発者がこのような区別できない複数のコールバックを処理できる場合です。You can raise an exception when your class explicitly cannot handle that situation, but assume that developers can handle these multiple indistinguishable callbacks

結果へのアクセスAccessing Results

進行状況のレポートProgress Reporting

  • 可能であれば、進行状況レポートをサポートします。Support progress reporting, if possible. これにより、開発者はクラスを使用する際に、より優れたアプリケーション ユーザー エクスペリエンスを提供できます。This enables developers to provide a better application user experience when they use your class.

  • ProgressChanged または MethodNameProgressChanged イベントを実装する場合は、操作の MethodNameCompleted イベントが発生した後で、特定の非同期操作についてそのようなイベントが発生していないようにしてください。If you implement a ProgressChanged or MethodNameProgressChanged event, ensure that there are no such events raised for a particular asynchronous operation after that operation's MethodNameCompleted event has been raised.

  • 標準 ProgressChangedEventArgs の値が設定される場合は、ProgressPercentage が常にパーセンテージとして解釈できるようにしてください。If the standard ProgressChangedEventArgs is being populated, ensure that the ProgressPercentage can always be interpreted as a percentage. パーセンテージは正確である必要はありませんが、パーセンテージを表している必要があります。The percentage does not need to be accurate, but it should represent a percentage. 進行状況レポート メトリックがパーセンテージ以外でなければならない場合は、ProgressChangedEventArgs クラスからクラスを派生し、ProgressPercentage は 0 のままにしておきます。If your progress reporting metric must be something other than a percentage, derive a class from the ProgressChangedEventArgs class and leave ProgressPercentage at 0. パーセンテージ以外のレポート メトリックは使用しないでください。Avoid using a reporting metric other than a percentage.

  • ProgressChanged イベントが、適切なスレッドでアプリケーション サイクルの適切な時点で発生するようにしてください。Ensure that the ProgressChanged event is raised on the appropriate thread and at the appropriate time in the application lifecycle. 詳細については、「スレッド処理およびコンテキスト」を参照してください。For more information, see the Threading and Contexts section.

IsBusy 実装IsBusy Implementation

  • クラスが複数の同時呼び出しをサポートしている場合は、IsBusy プロパティを公開しないでください。Do not expose an IsBusy property if your class supports multiple concurrent invocations. たとえば XML Web サービス プロキシは、非同期メソッドの複数同時呼び出しをサポートしているため、IsBusy プロパティを公開しません。For example, XML Web service proxies do not expose an IsBusy property because they support multiple concurrent invocations of asynchronous methods.

  • IsBusy プロパティは、MethodNameAsync メソッドが呼び出されてから、MethodNameCompleted イベントが発生するまでの間は、true を返す必要があります。The IsBusy property should return true after the MethodNameAsync method has been called and before the MethodNameCompleted event has been raised. それ以外の場合は、false を返す必要があります。Otherwise it should return false. BackgroundWorker プロパティを公開するクラスの例として、WebClient および IsBusy コンポーネントがあります。The BackgroundWorker and WebClient components are examples of classes that expose an IsBusy property.

キャンセルCancellation

  • 可能であれば、キャンセルをサポートしてください。Support cancellation, if possible. これにより、開発者はクラスを使用する際に、より優れたアプリケーション ユーザー エクスペリエンスを提供できます。This enables developers to provide a better application user experience when they use your class.

  • キャンセルの場合、Cancelled オブジェクトに AsyncCompletedEventArgs フラグを設定します。In the case of cancellation, set the Cancelled flag in the AsyncCompletedEventArgs object.

  • 結果にアクセスしようとすると、操作がキャンセルされたことを示す InvalidOperationException が発生するようにしてください。Ensure that any attempt to access the result raises an InvalidOperationException stating that the operation was canceled. この検証を行うには、AsyncCompletedEventArgs.RaiseExceptionIfNecessary メソッドを使用します。Use the AsyncCompletedEventArgs.RaiseExceptionIfNecessary method to perform this verification.

  • キャンセル メソッドの呼び出しは常に正常に戻り、例外を発生させないようにしてください。Ensure that calls to a cancellation method always return successfully, and never raise an exception. 一般に、特定の時点で操作が実際にキャンセル可能かどうかと、前に発行したキャンセルが正常に実行されたかどうかはクライアントに通知されません。In general, a client is not notified as to whether an operation is truly cancelable at any given time, and is not notified as to whether a previously issued cancellation has succeeded. ただし、キャンセルが正常に完了すると常にアプリケーションに通知が送られます。これは、アプリケーションが完了ステータスに関与しているためです。However, the application will always be given notification when a cancellation succeeded, because the application takes part in the completion status.

  • 操作がキャンセルされた場合は、MethodNameCompleted イベントを発生させます。Raise the MethodNameCompleted event when the operation is canceled.

エラーおよび例外Errors and Exceptions

  • 非同期操作で発生した例外をすべてキャッチし、その例外の AsyncCompletedEventArgs.Error プロパティの値を設定します。Catch any exceptions that occur in the asynchronous operation and set the value of the AsyncCompletedEventArgs.Error property to that exception.

スレッド処理およびコンテキストThreading and Contexts

クラスを正しく操作するには、特定のアプリケーション モデル (ASP.NET および Windows フォーム アプリケーションを含む) の適切なスレッドまたはコンテキストで、クライアントのイベント ハンドラーが呼び出されることが重要です。For correct operation of your class, it is critical that the client's event handlers are invoked on the proper thread or context for the given application model, including ASP.NET and Windows Forms applications. 非同期クラスがどのアプリケーション モデルでも正しく動作するように、AsyncOperationAsyncOperationManager という 2 つの重要なヘルパー クラスが用意されています。Two important helper classes are provided to ensure that your asynchronous class behaves correctly under any application model: AsyncOperation and AsyncOperationManager.

AsyncOperationManager にはメソッド CreateOperation が含まれています。このメソッドは AsyncOperation を返します。AsyncOperationManager provides one method, CreateOperation, which returns an AsyncOperation. MethodNameAsync メソッドは CreateOperation を呼び出し、クラスは返される AsyncOperation を使用して非同期タスクの有効期間を追跡します。Your MethodNameAsync method calls CreateOperation and your class uses the returned AsyncOperation to track the lifetime of the asynchronous task.

進行状況、インクリメンタル結果、および完了をクライアントに報告するため、PostOperationCompleted メソッドと AsyncOperation メソッドを呼び出します。To report progress, incremental results, and completion to the client, call the Post and OperationCompleted methods on the AsyncOperation. AsyncOperation は、クライアントのイベント ハンドラーに対する呼び出しを適切なスレッドまたはコンテキストにマーシャリングします。AsyncOperation is responsible for marshaling calls to the client's event handlers to the proper thread or context.

注意

アプリケーション モデルのポリシーに対し明示的に準拠しないものの、イベント ベースの非同期パターンを使用する他のメリットを利用したい場合は、これらの規則を回避できます。You can circumvent these rules if you explicitly want to go against the policy of the application model, but still benefit from the other advantages of using the Event-based Asynchronous Pattern. たとえば、Windows Forms でのクラス操作をフリー スレッド化するとします。For example, you may want a class operating in Windows Forms to be free threaded. 開発者がフリー スレッド化クラスの暗黙的な制限を理解している場合は、フリースレッド化クラスを作成できます。You can create a free threaded class, as long as developers understand the implied restrictions. コンソール アプリケーションは Post 呼び出しの実行を同期しません。Console applications do not synchronize the execution of Post calls. これが原因で、ProgressChanged イベントが正しくない順序で発生することがあります。This can cause ProgressChanged events to be raised out of order. Post 呼び出しを順次実行するには、System.Threading.SynchronizationContext クラスを実装およびインストールします。If you wish to have serialized execution of Post calls, implement and install a System.Threading.SynchronizationContext class.

AsyncOperationAsyncOperationManager を使用して非同期操作を使用可能にする方法の詳細については、「方法: イベントベースの非同期パターンをサポートするコンポーネントを実装する」を参照してください。For more information about using AsyncOperation and AsyncOperationManager to enable your asynchronous operations, see How to: Implement a Component That Supports the Event-based Asynchronous Pattern.

ガイドラインGuidelines

  • 各メソッド呼び出しが相互に独立していることが理想的です。Ideally, each method invocation should be independent of others. 呼び出しを共有リソースと組み合わせないでください。You should avoid coupling invocations with shared resources. リソースを複数の呼び出し間で共有する場合は、実装に適切な同期メカニズムを提供する必要があります。If resources are to be shared among invocations, you will need to provide a proper synchronization mechanism in your implementation.

  • クライアントが同期を実装する必要がある設計は推奨されません。Designs that require the client to implement synchronization are discouraged. たとえば、グローバルな静的オブジェクトをパラメータとして受け取る非同期メソッドがあり、この非同期メソッドを同時に複数呼び出すと、データの破損またはデッドロックが発生することがあります。For example, you could have an asynchronous method that receives a global static object as a parameter; multiple concurrent invocations of such a method could result in data corruption or deadlocks.

  • 複数呼び出しのオーバーロード (シグネチャの userState) を使用してメソッドを実装する場合、ユーザー状態、タスク ID、対応する保留操作のコレクションをクラスで管理する必要があります。If you implement a method with the multiple-invocation overload (userState in the signature), your class will need to manage a collection of user states, or task IDs, and their corresponding pending operations. さまざまな呼び出しでこのコレクションの lock オブジェクトが追加および削除されるため、このコレクションを userState 領域で保護する必要があります。This collection should be protected with lock regions, because the various invocations add and remove userState objects in the collection.

  • 可能かつ適切な場合は、CompletedEventArgs クラスの再利用を検討してください。Consider reusing CompletedEventArgs classes where feasible and appropriate. この場合、特定のデリゲートと EventArgs 型が 1 つのメソッドに関連付けられていないため、名前付けにはメソッド名との整合性がありません。In this case, the naming is not consistent with the method name, because a given delegate and EventArgs type are not tied to a single method. ただし、開発者に対して EventArgs のプロパティから取得した値をキャストすることを強制することはできません。However, forcing developers to cast the value retrieved from a property on the EventArgs is never acceptable.

  • Component から派生したクラスを編集する場合、独自の SynchronizationContext クラスを実装およびインストールしないでください。If you are authoring a class that derives from Component, do not implement and install your own SynchronizationContext class. 使用される SynchronizationContext を制御するのは、コンポーネントではなくアプリケーション モデルです。Application models, not components, control the SynchronizationContext that is used.

  • どのようなマルチスレッドを使用する場合でも、深刻かつ複雑なバグが発生する可能性があります。When you use multithreading of any sort, you potentially expose yourself to very serious and complex bugs. マルチスレッドを使用するソリューションを実装する前に、「マネージド スレッド処理の実施」を参照してください。Before implementing any solution that uses multithreading, see Managed Threading Best Practices.

関連項目See also