Meilleures pratiques pour implémenter le modèle asynchrone basé sur des événementsBest Practices for Implementing the Event-based Asynchronous Pattern

Le modèle asynchrone basé sur les événements vous permet d’exposer efficacement un comportement asynchrone dans les classes, en utilisant une sémantique d’événement et de délégué familière.The Event-based Asynchronous Pattern provides you with an effective way to expose asynchronous behavior in classes, with familiar event and delegate semantics. Pour implémenter le modèle asynchrone basé sur les événements, vous devez respecter certaines contraintes liées au comportement.To implement Event-based Asynchronous Pattern, you need to follow some specific behavioral requirements. Les sections ci-après décrivent les exigences et les recommandations que vous devez prendre en compte quand vous implémentez une classe qui suit le modèle asynchrone basé sur les événements.The following sections describe requirements and guidelines you should consider when you implement a class that follows the Event-based Asynchronous Pattern.

Pour une présentation, consultez Implémentation du modèle asynchrone basé sur des événements.For an overview, see Implementing the Event-based Asynchronous Pattern.

Garanties de comportement obligatoiresRequired Behavioral Guarantees

Si vous implémentez le modèle asynchrone basé sur les événements, vous devez fournir une série de garanties pour que votre classe se comporte correctement et que les clients de votre classe puissent s'appuyer sur ce comportement.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.

AchèvementCompletion

Appelez toujours le gestionnaire d’événements MethodNameCompleted quand l’opération s’est terminée correctement, ou bien en cas d’erreur ou d’annulation.Always invoke the MethodNameCompleted event handler when you have successful completion, an error, or a cancellation. Les applications ne doivent jamais rencontrer de situation dans laquelle elles demeurent inactives sans jamais terminer l'opération en cours.Applications should never encounter a situation where they remain idle and completion never occurs. L’exception à cette règle est si l’opération asynchrone elle-même est conçue de manière à ne jamais se terminer.One exception to this rule is if the asynchronous operation itself is designed so that it never completes.

Événement Completed et classe EventArgsCompleted Event and EventArgs

Pour chaque méthode MethodNameAsync distincte, appliquez les contraintes de conception suivantes :For each separate MethodNameAsync method, apply the following design requirements:

  • Définissez un événement MethodNameCompleted sur la même classe que la méthode.Define a MethodNameCompleted event on the same class as the method.

  • Définissez une classe EventArgs et le délégué associé pour l’événement MethodNameCompleted qui dérive de la classe AsyncCompletedEventArgs.Define an EventArgs class and accompanying delegate for the MethodNameCompleted event that derives from the AsyncCompletedEventArgs class. Le nom de classe par défaut doit être de la forme NomMéthodeCompletedEventArgs.The default class name should be of the form MethodNameCompletedEventArgs.

  • Vérifiez que la classe EventArgs est spécifique des valeurs de retour de la méthode nom_méthode.Ensure that the EventArgs class is specific to the return values of the MethodName method. Quand vous utilisez la classe EventArgs, vous ne devez jamais obliger les développeurs à effectuer un transtypage du résultat.When you use the EventArgs class, you should never require developers to cast the result.

    L'exemple de code suivant montre une bonne implémentation de cette contrainte de conception, suivie d'une mauvaise.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);  
}  
  • Ne définissez pas de classe EventArgs pour les méthodes de retour qui retournent void.Do not define an EventArgs class for returning methods that return void. À la place, utilisez une instance de la classe AsyncCompletedEventArgs.Instead, use an instance of the AsyncCompletedEventArgs class.

  • Veillez à toujours déclencher l’événement NomMéthodeCompleted.Ensure that you always raise the MethodNameCompleted event. Cet événement doit être déclenché quand l'opération s'est terminée correctement, ou bien en cas d'erreur ou d'annulation.This event should be raised on successful completion, on an error, or on cancellation. Les applications ne doivent jamais rencontrer de situation dans laquelle elles demeurent inactives sans jamais terminer l'opération en cours.Applications should never encounter a situation where they remain idle and completion never occurs.

  • Veillez à intercepter toutes les exceptions qui se produisent au cours de l'opération asynchrone et à affecter l'exception interceptée à la propriété Error.Ensure that you catch any exceptions that occur in the asynchronous operation and assign the caught exception to the Error property.

  • Si une erreur se produit pendant l'exécution de la tâche, les résultats doivent être inaccessibles.If there was an error completing the task, the results should not be accessible. Quand la propriété Error n'a pas pour valeur null, assurez-vous que l'accès à toute propriété dans la structure EventArgs lève une exception.When the Error property is not null, ensure that accessing any property in the EventArgs structure raises an exception. Utilisez la méthode RaiseExceptionIfNecessary pour effectuer cette vérification.Use the RaiseExceptionIfNecessary method to perform this verification.

  • Modélisez un délai au terme duquel une erreur se produit.Model a time out as an error. Quand un délai arrive à expiration, déclenchez l’événement NomMéthodeCompleted et affectez une TimeoutException à la propriété Error.When a time out occurs, raise the MethodNameCompleted event and assign a TimeoutException to the Error property.

  • Si votre classe prend en charge plusieurs appels simultanés, vérifiez que l’événement NomMéthodeCompleted contient l’objet userSuppliedState adéquat.If your class supports multiple concurrent invocations, ensure that the MethodNameCompleted event contains the appropriate userSuppliedState object.

  • Vérifiez que l’événement NomMéthodeCompleted est déclenché sur le thread approprié et au moment adéquat du cycle de vie de l’application.Ensure that the MethodNameCompleted event is raised on the appropriate thread and at the appropriate time in the application lifecycle. Pour plus d'informations, voir la section Thread et contextes.For more information, see the Threading and Contexts section.

Exécution d'opérations simultanémentSimultaneously Executing Operations

  • Si votre classe prend en charge plusieurs appels simultanés, autorisez le développeur à effectuer le suivi de chaque appel séparément en définissant la surcharge de NomMéthodeAsync qui prend un paramètre d’état objet, ou un ID de tâche, appelé userSuppliedState.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. Ce paramètre doit toujours être le dernier de la signature de la méthode NomMéthodeAsync.This parameter should always be the last parameter in the MethodNameAsync method's signature.

  • Si votre classe définit la surcharge de NomMéthodeAsync qui prend un paramètre d’état objet, ou un ID de tâche, veillez à effectuer le suivi de la durée de vie de l’opération avec cet ID de tâche, et à le fournir en retour au gestionnaire d’achèvement.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. Vous pouvez vous aider de classes d'assistance.There are helper classes available to assist. Pour plus d’informations sur la gestion de la concurrence, consultez Guide pratique pour implémenter un composant qui prend en charge le modèle asynchrone basé sur des événements.For more information on concurrency management, see How to: Implement a Component That Supports the Event-based Asynchronous Pattern.

  • Si votre classe définit la méthode NomMéthodeAsync sans le paramètre d’état et qu’elle ne prend pas en charge plusieurs appels simultanés, veillez à ce que toute tentative d’appel de NomMéthodeAsync avant que soit achevé l’appel précédent de NomMéthodeAsync lève une 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.

  • D’une façon générale, ne levez pas d’exception si la méthode NomMéthodeAsync sans le paramètre userSuppliedState est appelée plusieurs fois et que plusieurs opérations se trouvent ainsi en attente.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. Vous pouvez lever une exception quand votre classe ne peut pas explicitement gérer cette situation, mais partez du principe que les développeurs peuvent gérer ces différents rappels impossibles à distinguer.You can raise an exception when your class explicitly cannot handle that situation, but assume that developers can handle these multiple indistinguishable callbacks

Accès aux résultatsAccessing Results

Rapport de progressionProgress Reporting

  • Dans la mesure du possible, prenez en charge le rapport de progression.Support progress reporting, if possible. Cela permet aux développeurs de fournir une meilleure expérience utilisateur d'application quand ils utilisent votre classe.This enables developers to provide a better application user experience when they use your class.

  • Si vous implémentez un événement ProgressChanged ou NomMéthodeProgressChanged, vérifiez qu’aucun événement de ce type n’est déclenché pour une opération asynchrone en particulier après que l’événement NomMéthodeCompleted de cette opération a été déclenché.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.

  • Si la classe ProgressChangedEventArgs standard est remplie, assurez-vous que la propriété ProgressPercentage peut toujours être interprétée en tant que pourcentage.If the standard ProgressChangedEventArgs is being populated, ensure that the ProgressPercentage can always be interpreted as a percentage. La valeur de pourcentage n'a pas besoin d'être précise, mais elle doit représenter un pourcentage.The percentage does not need to be accurate, but it should represent a percentage. Si la mesure du rapport de progression doit être autre chose qu'un pourcentage, dérivez une classe de la classe ProgressChangedEventArgs et laissez ProgressPercentage défini sur 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. Évitez d'utiliser une mesure d'indication autre qu'un pourcentage.Avoid using a reporting metric other than a percentage.

  • Assurez-vous que l'événement ProgressChanged est déclenché sur le thread approprié et au moment adéquat pendant le cycle de vie de l'application.Ensure that the ProgressChanged event is raised on the appropriate thread and at the appropriate time in the application lifecycle. Pour plus d'informations, voir la section Thread et contextes.For more information, see the Threading and Contexts section.

Implémentation d'IsBusyIsBusy Implementation

  • N'exposez pas de propriété IsBusy si votre classe prend en charge plusieurs appels simultanés.Do not expose an IsBusy property if your class supports multiple concurrent invocations. Par exemple, les proxys de service web XML n'exposent pas de propriété IsBusy, car ils prennent en charge plusieurs appels simultanés de méthodes asynchrones.For example, XML Web service proxies do not expose an IsBusy property because they support multiple concurrent invocations of asynchronous methods.

  • La propriété IsBusy doit retourner true une fois que la méthode NomMéthodeAsync a été appelée et avant que l’événement NomMéthodeCompleted n’ait été déclenché.The IsBusy property should return true after the MethodNameAsync method has been called and before the MethodNameCompleted event has been raised. Sinon elle doit retourner false.Otherwise it should return false. Les composants BackgroundWorker et WebClient sont des exemples de classes qui exposent une propriété IsBusy.The BackgroundWorker and WebClient components are examples of classes that expose an IsBusy property.

AnnulationCancellation

  • Dans la mesure du possible, prenez en charge l'annulation.Support cancellation, if possible. Cela permet aux développeurs de fournir une meilleure expérience utilisateur d'application quand ils utilisent votre classe.This enables developers to provide a better application user experience when they use your class.

  • Dans le cas de l'annulation, définissez l'indicateur Cancelled dans l'objet AsyncCompletedEventArgs.In the case of cancellation, set the Cancelled flag in the AsyncCompletedEventArgs object.

  • Assurez-vous que toute tentative d'accéder au résultat lève une exception InvalidOperationException signalant que l'opération a été annulée.Ensure that any attempt to access the result raises an InvalidOperationException stating that the operation was canceled. Utilisez la méthode AsyncCompletedEventArgs.RaiseExceptionIfNecessary pour effectuer cette vérification.Use the AsyncCompletedEventArgs.RaiseExceptionIfNecessary method to perform this verification.

  • Assurez-vous que les appels d'une méthode d'annulation réussissent toujours et qu'ils ne déclenchent jamais d'exception.Ensure that calls to a cancellation method always return successfully, and never raise an exception. En général, un client n'est pas informé qu'il peut ou non annuler une opération à tout moment, ni qu'une annulation précédemment émise a effectivement réussi.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. Toutefois, l'application est toujours avertie qu'une annulation a réussi, car elle est impliquée dans la gestion de l'état d'achèvement.However, the application will always be given notification when a cancellation succeeded, because the application takes part in the completion status.

  • Déclenchez l’événement NomMéthodeCompleted quand l’opération est annulée.Raise the MethodNameCompleted event when the operation is canceled.

Erreurs et exceptionsErrors and Exceptions

  • Interceptez toute exception qui se produit pendant l'opération asynchrone et définissez la valeur de la propriété AsyncCompletedEventArgs.Error sur cette exception.Catch any exceptions that occur in the asynchronous operation and set the value of the AsyncCompletedEventArgs.Error property to that exception.

Thread et contextesThreading and Contexts

Pour que votre classe fonctionne correctement, il est primordial que les gestionnaires d'événements du client soient appelés sur le thread ou contexte adéquat en fonction du modèle d'application, notamment en ce qui concerne les applications Windows Forms et ASP.NETASP.NET.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.NETASP.NET and Windows Forms applications. Deux classes d'assistance importantes vous permettent de vous assurer que votre classe asynchrone se comporte correctement dans le cadre de n'importe quel modèle d'application : AsyncOperation et AsyncOperationManager.Two important helper classes are provided to ensure that your asynchronous class behaves correctly under any application model: AsyncOperation and AsyncOperationManager.

AsyncOperationManager fournit une méthode, CreateOperation, qui retourne un objet AsyncOperation.AsyncOperationManager provides one method, CreateOperation, which returns an AsyncOperation. Votre méthode NomMéthodeAsync appelle CreateOperation et votre classe utilise l’objet AsyncOperation retourné pour effectuer le suivi de la durée de vie de la tâche asynchrone.Your MethodNameAsync method calls CreateOperation and your class uses the returned AsyncOperation to track the lifetime of the asynchronous task.

Pour indiquer la progression, les résultats incrémentiels et l'achèvement au client, appelez les méthodes Post et OperationCompleted sur la classe AsyncOperation.To report progress, incremental results, and completion to the client, call the Post and OperationCompleted methods on the AsyncOperation. AsyncOperation prend en charge le marshaling des appels des gestionnaires d'événements du client en thread ou contexte adéquat.AsyncOperation is responsible for marshaling calls to the client's event handlers to the proper thread or context.

Notes

Vous pouvez contourner ces règles si vous souhaitez explicitement aller à l'encontre de la stratégie du modèle d'application, tout en tirant parti des autres avantages de l'utilisation du modèle asynchrone basé sur les événements.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. Par exemple, vous pouvez souhaiter qu'une classe fonctionnant dans Windows Forms soit libre de threads.For example, you may want a class operating in Windows Forms to be free threaded. Vous pouvez créer une classe libre de threads, sous réserve que les développeurs comprennent les restrictions que cela implique.You can create a free threaded class, as long as developers understand the implied restrictions. Les applications console ne synchronisent pas l'exécution des appels Post.Console applications do not synchronize the execution of Post calls. Cela peut entraîner le déclenchement d'événements ProgressChanged dans le désordre.This can cause ProgressChanged events to be raised out of order. Si vous souhaitez que l'exécution des appels Post soit sérialisée, implémentez et installez une classe System.Threading.SynchronizationContext.If you wish to have serialized execution of Post calls, implement and install a System.Threading.SynchronizationContext class.

Pour plus d’informations sur l’utilisation de AsyncOperation et AsyncOperationManager pour activer vos opérations asynchrones, consultez Guide pratique pour implémenter un composant qui prend en charge le modèle asynchrone basé sur des événements.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.

RecommandationsGuidelines

  • Dans l'idéal, chaque appel de méthode doit être indépendant des autres appels.Ideally, each method invocation should be independent of others. Vous devez éviter d'associer des appels qui partagent des ressources.You should avoid coupling invocations with shared resources. Si des ressources doivent être partagées par des appels, vous devez prévoir un mécanisme de synchronisation approprié dans votre implémentation.If resources are to be shared among invocations, you will need to provide a proper synchronization mechanism in your implementation.

  • Les conceptions qui obligent le client à implémenter une synchronisation sont déconseillées.Designs that require the client to implement synchronization are discouraged. Par exemple, dans le cas d'une méthode asynchrone qui reçoit un objet statique global comme paramètre, plusieurs appels simultanés de ce type de méthode peuvent entraîner une altération des données ou des interblocages.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.

  • Si vous implémentez une méthode avec la surcharge de plusieurs appels (userState dans la signature), votre classe doit gérer une collection d'états utilisateur, ou d'ID de tâche, ainsi que leurs opérations en attente correspondantes.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. Cette collection doit être protégée avec des régions lock, car les différents appels ajoutent et suppriment des objets userState dans la collection.This collection should be protected with lock regions, because the various invocations add and remove userState objects in the collection.

  • Pensez à réutiliser les classes CompletedEventArgs si cela est possible et approprié.Consider reusing CompletedEventArgs classes where feasible and appropriate. Dans ce cas, l'affectation des noms n'est pas cohérente avec le nom de la méthode, car un délégué et un type EventArgs donnés ne sont pas liés à une même méthode.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. Toutefois, évitez absolument que les développeurs soit obligés d'effectuer un transtypage de la valeur récupérée d'une propriété sur la classe EventArgs.However, forcing developers to cast the value retrieved from a property on the EventArgs is never acceptable.

  • Si vous créez une classe qui dérive de Component, n'implémentez pas et n'installez pas votre propre classe SynchronizationContext.If you are authoring a class that derives from Component, do not implement and install your own SynchronizationContext class. Ce sont les modèles d'application, pas les composants, qui contrôlent la classe SynchronizationContext utilisée.Application models, not components, control the SynchronizationContext that is used.

  • Quand vous utilisez un type quelconque de multithreading, vous vous exposez potentiellement à des bogues sérieux et complexes.When you use multithreading of any sort, you potentially expose yourself to very serious and complex bugs. Consultez les Meilleures pratiques pour le threading managé avant d'implémenter une solution qui utilise le multithreading.Before implementing any solution that uses multithreading, see Managed Threading Best Practices.

Voir aussiSee also