Implementar el modelo asincrónico basado en eventosImplementing the Event-based Asynchronous Pattern

Si está escribiendo una clase con algunas operaciones que pueden dar lugar a retrasos evidentes, considere la posibilidad de darle funcionalidad asincrónica implementando Información general sobre el modelo asincrónico basado en eventos.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.

El modelo asincrónico basado en eventos proporciona una forma estandarizada de empaquetar una clase que tenga características asincrónicas.The Event-based Asynchronous Pattern provides a standardized way to package a class that has asynchronous features. Si se implementa con clases auxiliares como AsyncOperationManager, la clase funciona correctamente en cualquier modelo de aplicación, incluidas aplicaciones de ASP.NET, de consola y de Windows Forms.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.

Para obtener un ejemplo que implemente el modelo asincrónico basado en eventos, consulte How to: Implement a Component That Supports the Event-based Asynchronous Pattern (Cómo: Implementar un componente que admita el modelo asincrónico basado en eventos).For an example that implements the Event-based Asynchronous Pattern, see How to: Implement a Component That Supports the Event-based Asynchronous Pattern.

Para las operaciones asincrónicas sencillas, puede encontrar el componente BackgroundWorker adecuado.For simple asynchronous operations, you may find the BackgroundWorker component suitable. Para más información sobre BackgroundWorker, vea Cómo: Ejecutar una operación en segundo plano.For more information about BackgroundWorker, see How to: Run an Operation in the Background.

En la siguiente lista se describen las características del modelo asincrónico basado en eventos tratadas en este tema.The following list describes the features of the Event-based Asynchronous Pattern discussed in this topic.

  • Oportunidades para implementar el modelo asincrónico basado en eventosOpportunities for Implementing the Event-based Asynchronous Pattern

  • Métodos asincrónicos de nomenclaturaNaming Asynchronous Methods

  • Opcionalmente, admiten la cancelaciónOptionally Support Cancellation

  • Opcionalmente, admiten la propiedad IsBusyOptionally Support the IsBusy Property

  • Opcionalmente, proporcionan compatibilidad para el informe de progresoOptionally Provide Support for Progress Reporting

  • Opcionalmente, proporcionan compatibilidad para devolver resultados incrementalesOptionally Provide Support for Returning Incremental Results

  • Control de los parámetros Out y Ref en los métodosHandling Out and Ref Parameters in Methods

Oportunidades para implementar el modelo asincrónico basado en eventosOpportunities for Implementing the Event-based Asynchronous Pattern

Considere la posibilidad de implementar el modelo asincrónico basado en eventos en estos casos:Consider implementing the Event-based Asynchronous Pattern when:

  • Los clientes de la clase no necesitan que los objetos WaitHandle y IAsyncResult estén disponibles para operaciones asincrónicas, lo que significa que el sondeo y WaitAll o WaitAny deberá crearlos el cliente.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.

  • Desea que el cliente administre las operaciones asincrónicas con el modelo delegado o de evento familiar.You want asynchronous operations to be managed by the client with the familiar event/delegate model.

Cualquier operación es candidata para una implementación asincrónica, pero deben tenerse en cuenta aquellas que espera que incurran en latencias de larga duración.Any operation is a candidate for an asynchronous implementation, but those you expect to incur long latencies should be considered. Son especialmente adecuadas las operaciones en las cuales los clientes llaman a un método y reciben una notificación de su conclusión, sin necesidad de ninguna otra intervención.Especially appropriate are operations in which clients call a method and are notified on completion, with no further intervention required. También son adecuadas aquellas que se ejecutan continuamente, notificando a los clientes de forma periódica del progreso, los resultados incrementales o los cambios de estado.Also appropriate are operations which run continuously, periodically notifying clients of progress, incremental results, or state changes.

Para más información sobre cómo decidir cuándo admitir el modelo asincrónico basado en eventos, consulte Decisión de cuándo implementar el modelo asincrónico basado en eventos.For more information on deciding when to support the Event-based Asynchronous Pattern, see Deciding When to Implement the Event-based Asynchronous Pattern.

Métodos asincrónicos de nomenclaturaNaming Asynchronous Methods

Para cada método sincrónico MethodName para el cual desee proporcionar un homólogo asincrónico:For each synchronous method MethodName for which you want to provide an asynchronous counterpart:

Defina un método MethodNameAsync que:Define a MethodNameAsync method that:

  • Devuelve void.Returns void.

  • Tome los mismos parámetros que el método MethodName.Takes the same parameters as the MethodName method.

  • Acepte varias invocaciones.Accepts multiple invocations.

Opcionalmente, defina una sobrecarga MethodNameAsync, idéntica a MethodNameAsync, pero con un parámetro con valor de objeto adicional denominado userState.Optionally define a MethodNameAsync overload, identical to MethodNameAsync, but with an additional object-valued parameter called userState. Haga esto si está preparado para administrar varias invocaciones simultáneas de su método, en cuyo caso, el valor userState se entregará de nuevo a todos los controladores de eventos para diferenciar las invocaciones del método.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. También puede decidir hacerlo sencillamente como un lugar donde almacenar el estado del usuario para su posterior recuperación.You may also choose to do this simply as a place to store user state for later retrieval.

Para cada signatura del método MethodNameAsync independiente:For each separate MethodNameAsync method signature:

  1. Defina el siguiente evento en la misma clase que el método:Define the following event in the same class as the method:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. Defina el delegado siguiente y AsyncCompletedEventArgs.Define the following delegate and AsyncCompletedEventArgs. Es probable que estos se definan fuera de la propia clase, pero en el mismo espacio de nombres.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; }
    }
    
    • Asegúrese de que la clase MethodNameCompletedEventArgs expone sus miembros como propiedades de solo lectura y no como campos, ya que estos impiden el enlace de datos.Ensure that the MethodNameCompletedEventArgs class exposes its members as read-only properties, and not fields, as fields prevent data binding.

    • No defina ninguna clase derivada de AsyncCompletedEventArgs para métodos que no producen resultados.Do not define any AsyncCompletedEventArgs-derived classes for methods that do not produce results. Simplemente se usa una instancia de AsyncCompletedEventArgs.Simply use an instance of AsyncCompletedEventArgs itself.

      Nota

      Es perfectamente aceptable, cuando sea posible y apropiado, para reutilizar el delegado y los tipos AsyncCompletedEventArgs.It is perfectly acceptable, when feasible and appropriate, to reuse delegate and AsyncCompletedEventArgs types. En este caso, la nomenclatura no será tan coherente con el nombre del método, ya que ni un delegado especificado ni AsyncCompletedEventArgs se vincularán a un solo método.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.

Opcionalmente, admiten la cancelaciónOptionally Support Cancellation

Si su clase admite la cancelación de operaciones asincrónicas, la cancelación debe exponerse al cliente como se describe a continuación.If your class will support canceling asynchronous operations, cancellation should be exposed to the client as described below. Tenga en cuenta que hay dos puntos de decisión que deben alcanzarse antes de definir la compatibilidad de cancelación:Note that there are two decision points that need to be reached before defining your cancellation support:

  • ¿Tiene su clase, incluidas las futuras adiciones a la misma previstas, solo una operación asincrónica que admita la cancelación?Does your class, including future anticipated additions to it, have only one asynchronous operation that supports cancellation?

  • ¿Pueden las operaciones asincrónicas que admiten la cancelación admitir varias operaciones pendientes?Can the asynchronous operations that support cancellation support multiple pending operations? Es decir, ¿toma el método MethodNameAsync un parámetro userState y permite varias invocaciones antes de esperar a que finalice alguna?That is, does the MethodNameAsync method take a userState parameter, and does it allow multiple invocations before waiting for any to finish?

Use las respuestas a estas dos preguntas en la siguiente tabla para determinar cuál debe ser la signatura para el método de cancelación.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

Varias operaciones simultáneas compatiblesMultiple Simultaneous Operations Supported Solo una operación cada vezOnly One Operation at a Time
Una operación asincrónica en toda la claseOne Async Operation in entire class Sub MethodNameAsyncCancel(ByVal userState As Object) Sub MethodNameAsyncCancel()
Varias operaciones asincrónicas en la claseMultiple Async Operations in class Sub CancelAsync(ByVal userState As Object) Sub CancelAsync()

C#C#

Varias operaciones simultáneas compatiblesMultiple Simultaneous Operations Supported Solo una operación cada vezOnly One Operation at a Time
Una operación asincrónica en toda la claseOne Async Operation in entire class void MethodNameAsyncCancel(object userState); void MethodNameAsyncCancel();
Varias operaciones asincrónicas en la claseMultiple Async Operations in class void CancelAsync(object userState); void CancelAsync();

Si define el método CancelAsync(object userState), los clientes deben ser cuidadosos al elegir su valor de estado para poder diferenciar entre todos los métodos asincrónicos invocados en el objeto y no solo entre todas las invocaciones de un solo método asincrónico.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.

La decisión de asignar un nombre a la versión de operación asincrónica única MethodNameAsyncCancel se basa en la capacidad de detectar más fácilmente el método en un entorno de diseño como IntelliSense de Visual Studio.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. Esto agrupa a los miembros relacionados y los diferencia de otros miembros que no tienen nada que ver con la funcionalidad asincrónica.This groups the related members and distinguishes them from other members that have nothing to do with asynchronous functionality. Si espera que pueda haber agregadas operaciones asincrónicas adicionales en versiones posteriores, es mejor definir CancelAsync.If you expect that there may be additional asynchronous operations added in subsequent versions, it is better to define CancelAsync.

No defina varios métodos de la tabla anterior en la misma clase.Do not define multiple methods from the table above in the same class. No tendrá sentido o desordenará la interfaz de clase con una proliferación de métodos.That will not make sense, or it will clutter the class interface with a proliferation of methods.

Normalmente, estos métodos volverán de inmediato y la operación puede cancelarse, o bien puede no hacerlo en realidad.These methods typically will return immediately, and the operation may or may not actually cancel. En el controlador de eventos para el evento MethodNameCompleted, el objeto MethodNameCompletedEventArgs contiene un campo Cancelled que los clientes pueden usar para determinar si se ha producido la cancelación.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.

Cumpla la semántica de cancelación descrita en Procedimientos recomendados para implementar el modelo asincrónico basado en eventos.Abide by the cancellation semantics described in Best Practices for Implementing the Event-based Asynchronous Pattern.

Opcionalmente, admiten la propiedad IsBusyOptionally Support the IsBusy Property

Si la clase no admite varias invocaciones simultáneas, considere la posibilidad de exponer una propiedad IsBusy.If your class does not support multiple concurrent invocations, consider exposing an IsBusy property. Esto permite a los desarrolladores determinar si un método MethodNameAsync se ejecuta sin detectar una excepción desde el método MethodNameAsync.This allows developers to determine whether a MethodNameAsync method is running without catching an exception from the MethodNameAsync method.

Cumpla la semántica de IsBusy descrita en Procedimientos recomendados para implementar el modelo asincrónico basado en eventos.Abide by the IsBusy semantics described in Best Practices for Implementing the Event-based Asynchronous Pattern.

Opcionalmente, proporcionan compatibilidad para el informe de progresoOptionally Provide Support for Progress Reporting

Suele ser recomendable que una operación asincrónica informe sobre el progreso durante su funcionamiento.It is frequently desirable for an asynchronous operation to report progress during its operation. Para ello, el modelo asincrónico basado en eventos proporciona una directriz.The Event-based Asynchronous Pattern provides a guideline for doing so.

  • Opcionalmente, defina un evento que la operación asincrónica va a generar y que se va a invocar en el subproceso adecuado.Optionally define an event to be raised by the asynchronous operation and invoked on the appropriate thread. El objeto ProgressChangedEventArgs lleva un indicador de progreso con valor entero que se espera que se sitúe entre 0 y 100.The ProgressChangedEventArgs object carries an integer-valued progress indicator that is expected to be between 0 and 100.

  • Asigne un nombre a este evento como se muestra a continuación:Name this event as follows:

    • ProgressChanged si la clase tiene varias operaciones asincrónicas (o se espera que crezca para incluir varias operaciones asincrónicas en futuras versiones);ProgressChanged if the class has multiple asynchronous operations (or is expected to grow to include multiple asynchronous operations in future versions);

    • MethodNameProgressChanged si la clase tiene una sola operación asincrónica.MethodNameProgressChanged if the class has a single asynchronous operation.

    Esta opción de nomenclatura va paralela a aquella creada para el método de cancelación, como se describe en la sección Opcionalmente, admiten la cancelación.This naming choice parallels that made for the cancellation method, as described in the Optionally Support Cancellation section.

Este evento debería utilizar la firma de delegado ProgressChangedEventHandler y la clase ProgressChangedEventArgs.This event should use the ProgressChangedEventHandler delegate signature and the ProgressChangedEventArgs class. De forma alternativa, si puede proporcionarse un indicador de progreso más específico de dominio (por ejemplo, bytes leídos y total de bytes para una operación de descarga), debe definir una clase derivada de 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.

Tenga en cuenta que solo hay un evento ProgressChanged o MethodNameProgressChanged para la clase, independientemente del número de métodos asincrónicos que admite.Note that there is only one ProgressChanged or MethodNameProgressChanged event for the class, regardless of the number of asynchronous methods it supports. Se espera que los clientes usen el objeto userState que se pasa a los métodos MethodNameAsync para diferenciar entre las actualizaciones de progreso en varias operaciones simultáneas.Clients are expected to use the userState object that is passed to the MethodNameAsync methods to distinguish among progress updates on multiple concurrent operations.

Puede haber situaciones en las que varias operaciones admitan el progreso y cada una devuelva un indicador para el progreso diferente.There may be situations in which multiple operations support progress and each returns a different indicator for progress. En este caso, un solo evento ProgressChanged no es adecuado, pudiendo considerar la posibilidad de admitir varios eventos ProgressChanged.In this case, a single ProgressChanged event is not appropriate, and you may consider supporting multiple ProgressChanged events. En este caso, use un patrón de nombres de MethodNameProgressChanged para cada método MethodNameAsync.In this case use a naming pattern of MethodNameProgressChanged for each MethodNameAsync method.

Cumpla la semántica de notificación sobre el progreso descrita en Procedimientos recomendados para implementar el modelo asincrónico basado en eventos.Abide by the progress-reporting semantics described Best Practices for Implementing the Event-based Asynchronous Pattern.

Opcionalmente, proporcionan compatibilidad para devolver resultados incrementalesOptionally Provide Support for Returning Incremental Results

A veces, una operación asincrónica puede devolver resultados incrementales antes de finalizar.Sometimes an asynchronous operation can return incremental results prior to completion. Hay una serie de opciones que se pueden usar para admitir este escenario.There are a number of options that can be used to support this scenario. A continuación, se presentan algunos ejemplos.Some examples follow.

Clase de operación únicaSingle-operation Class

Si la clase solo admite una sola operación asincrónica y dicha operación puede devolver resultados incrementales, haga lo siguiente:If your class only supports a single asynchronous operation, and that operation is able to return incremental results, then:

  • Amplíe el tipo ProgressChangedEventArgs para llevar los datos del resultado incremental y defina un evento MethodNameProgressChanged con estos datos extendidos.Extend the ProgressChangedEventArgs type to carry the incremental result data, and define a MethodNameProgressChanged event with this extended data.

  • Genere este evento MethodNameProgressChanged cuando haya un resultado incremental que notificar.Raise this MethodNameProgressChanged event when there is an incremental result to report.

Esta solución se aplica de forma específica a una clase de operación asincrónica única porque no hay ningún problema con que el mismo evento se produzca para devolver resultados incrementales en "todas las operaciones", como hace el evento 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.

Clase de varias operaciones con resultados incrementales homogéneosMultiple-operation Class with Homogeneous Incremental Results

En este caso, la clase admite varios métodos asincrónicos, cada uno capaz de devolver resultados incrementales. Además, todos estos resultados incrementales tienen el mismo tipo de datos.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.

Siga el modelo descrito anteriormente para las clases de operación única, ya que la misma estructura EventArgs funcionará para todos los resultados incrementales.Follow the model described above for single-operation classes, as the same EventArgs structure will work for all incremental results. Defina un evento ProgressChanged en lugar de un evento MethodNameProgressChanged, ya que se aplica a varios métodos asincrónicos.Define a ProgressChanged event instead of a MethodNameProgressChanged event, since it applies to multiple asynchronous methods.

Clase de varias operaciones con resultados incrementales heterogéneosMultiple-operation Class with Heterogeneous Incremental Results

Si la clase admite varios métodos asincrónicos, devolviendo cada uno un tipo de datos diferente, debe hacer lo siguiente:If your class supports multiple asynchronous methods, each returning a different type of data, you should:

  • Separe el informe de resultados del informe de progreso.Separate your incremental result reporting from your progress reporting.

  • Defina un evento MethodNameProgressChanged independiente con el elemento EventArgs adecuado para cada método asincrónico con el fin de controlar los datos del resultado incremental de ese método.Define a separate MethodNameProgressChanged event with appropriate EventArgs for each asynchronous method to handle that method's incremental result data.

Invoque ese controlador de eventos en el subproceso adecuado como se describe en Procedimientos recomendados para implementar el modelo asincrónico basado en eventos.Invoke that event handler on the appropriate thread as described in Best Practices for Implementing the Event-based Asynchronous Pattern.

Control de los parámetros Out y Ref en los métodosHandling Out and Ref Parameters in Methods

Aunque, en general, no se aconseja el uso de out y ref en .NET Framework, estas son las reglas que se deben seguir cuando estén presentes: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:

Dado un método sincrónico MethodName:Given a synchronous method MethodName:

  • Los parámetros out de MethodName no deben formar parte de MethodNameAsync.out parameters to MethodName should not be part of MethodNameAsync. En su lugar, deben formar parte de MethodNameCompletedEventArgs con el mismo nombre que su parámetro equivalente en MethodName (a menos que haya un nombre más adecuado).Instead, they should be part of MethodNameCompletedEventArgs with the same name as its parameter equivalent in MethodName (unless there is a more appropriate name).

  • Los parámetros ref de MethodName deben aparecer como parte de MethodNameAsync y como parte de MethodNameCompletedEventArgs con el mismo nombre que su parámetro equivalente en MethodName (a menos que haya un nombre más adecuado).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).

Por ejemplo, dado: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);

El método asincrónico y su clase AsyncCompletedEventArgs tendrían un aspecto similar a este: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; };
}

Vea tambiénSee also