Implementando o padrão assíncrono baseado em evento
Se você estiver escrevendo uma classe com algumas operações que possam causar atrasos notáveis, considere a opção de fornecer funcionalidade assíncrona Implementando o Padrão assíncrono baseado em evento.
O Padrão assíncrono baseado em evento fornece uma maneira padronizada de empacotar uma classe que tem recursos assíncronos. Se for implementada com classes auxiliares como AsyncOperationManager, sua classe funcionará corretamente em qualquer modelo de aplicativo, incluindo ASP.NET, aplicativos de Console e aplicativos do Windows Forms.
Para obter um exemplo que implementa o Padrão assíncrono baseado em evento, consulte Como implementar um componente compatível com o padrão assíncrono baseado em evento.
Para operações assíncronas simples, você pode obter o componente BackgroundWorker adequado. Para saber mais sobre BackgroundWorker, consulte Como executar uma operação em segundo plano.
A lista a seguir descreve os recursos do padrão assíncrono baseado em evento discutidos neste tópico.
Oportunidades para a implementação do Padrão assíncrono baseado em evento
Nomenclatura de métodos assíncronos
Suporte opcional de cancelamento
Suporte opcional da propriedade IsBusy
Fornecimento opcional de suporte para relatórios de progresso
Fornecimento opcional de suporte para retorno de resultados incrementais
Tratamento de parâmetros Out e Ref em métodos
Oportunidades para a implementação do Padrão assíncrono baseado em evento
Considere a implementação do Padrão assíncrono baseado em evento quando:
Os clientes de sua classe não precisam de objetos WaitHandle e IAsyncResult disponíveis para operações assíncronas, ou seja, a sondagem e WaitAll ou WaitAny deverão ser compilados pelo cliente.
Você quer que as operações assíncronas sejam gerenciadas pelo cliente com o modelo de evento/delegado conhecido.
Qualquer operação é candidata para uma implementação assíncrona, mas aquelas com expectativa de latência longas devem ser consideradas. Operações nas quais os clientes chamam um método e são notificados após a conclusão, sem necessidade de intervenção adicional, são especialmente apropriadas. Também são apropriadas as operações executadas continuamente, notificando periodicamente os clientes sobre o andamento, resultados incrementais ou alterações de estado.
Para saber mais sobre como decidir pelo suporte ao Padrão assíncrono baseado em evento, confira Decidir quando implementar o Padrão assíncrono baseado em evento.
Nomenclatura de métodos assíncronos
Para cada método síncrono MethodName para o qual você deseja fornecer uma contraparte assíncrona:
Defina um método MethodNameAsync que:
Retorna
void
.Usa os mesmos parâmetros que o método MethodName.
Aceita várias invocações.
Opcionalmente, defina uma sobrecarga MethodNameAsync, idêntica ao MethodNameAsync, mas com um parâmetro adicional com valor de objeto chamado userState
. Faça isso se você estiver preparado para gerenciar várias chamadas simultâneas de seu método. Nesse caso, o valor userState
será entregue novamente para todos os manipuladores de eventos a fim de distinguir invocações do método. Você também pode optar por fazer isso simplesmente como um local para armazenar o estado do usuário para recuperação posterior.
Para cada assinatura de método MethodNameAsync separada:
Defina o evento a seguir na mesma classe que o método:
Public Event MethodNameCompleted As MethodNameCompletedEventHandler
public event MethodNameCompletedEventHandler MethodNameCompleted;
Defina o representante a seguir e AsyncCompletedEventArgs. Provavelmente, eles serão definidos fora da própria classe, mas no mesmo 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; } }
Certifique-se de que a classe MethodNameCompletedEventArgs expõe seus membros como propriedades somente leitura, e não como campos, pois os campos impedem a associação de dados.
Não defina qualquer classe derivada de AsyncCompletedEventArgs para métodos que não produzem resultados. Simplesmente use uma instância do próprio AsyncCompletedEventArgs.
Observação
É perfeitamente aceitável, quando apropriada e plausível, a reutilização de delegados e tipos AsyncCompletedEventArgs. Nesse caso, a nomenclatura não será consistente com o nome do método, pois um delegado e AsyncCompletedEventArgs específicos não ficarão vinculados a um único método.
Suporte opcional de cancelamento
Se sua classe oferecer suporte ao cancelamento de operações assíncronas, o cancelamento deverá ser exposto ao cliente, conforme descrito abaixo. Há dois pontos de decisão que precisam ser alcançados antes de definir o suporte ao cancelamento:
- Sua classe, incluindo acréscimos futuros antecipados, tem apenas uma operação assíncrona que oferece suporte ao cancelamento?
- As operações assíncronas que oferecem suporte ao cancelamento podem dar suporte a várias operações pendentes? Ou seja, o método MethodNameAsync usa um parâmetro
userState
e ele permite várias invocações antes da conclusão de alguma?
Use as respostas para essas duas perguntas na tabela abaixo para determinar qual deve ser a assinatura para o método de cancelamento.
Visual Basic
Várias operações simultâneas com suporte | Apenas uma operação por vez | |
---|---|---|
Uma operação assíncrona em toda a classe | Sub MethodNameAsyncCancel(ByVal userState As Object) |
Sub MethodNameAsyncCancel() |
Várias operações assíncronas em classe | Sub CancelAsync(ByVal userState As Object) |
Sub CancelAsync() |
C#
Várias operações simultâneas com suporte | Apenas uma operação por vez | |
---|---|---|
Uma operação assíncrona em toda a classe | void MethodNameAsyncCancel(object userState); |
void MethodNameAsyncCancel(); |
Várias operações assíncronas em classe | void CancelAsync(object userState); |
void CancelAsync(); |
Se você definir o método CancelAsync(object userState)
, os clientes deverão ter cuidado ao escolher seus valores de estado a fim de torná-los capazes de distinguir entre todos os métodos assíncronos invocados no objeto, e não apenas entre todas as invocações de um único método assíncrono.
A decisão de nomear a versão de operação assíncrona única MethodNameAsyncCancel tem base em ser capaz de descobrir mais facilmente o método em um ambiente de design como IntelliSense do Visual Studio. Isso agrupa os membros relacionados e os distingue de outros membros que não têm qualquer relação com funcionalidade assíncrona. Se você espera que outras operações assíncronas sejam adicionadas em versões subsequentes, é melhor definir CancelAsync
.
Não defina vários métodos da tabela acima na mesma classe. Isso não fará sentido, ou sobrecarregará a interface de classe com a proliferação de métodos.
Normalmente, esses métodos retornarão imediatamente, e a operação poderá ou não ser realmente cancelada. No manipulador de eventos do evento MethodNameCompleted, o objeto MethodNameCompletedEventArgs contém um campo Cancelled
, o qual os clientes podem usar para determinar se o cancelamento ocorreu.
Obedeça à semântica de cancelamento descrita em Melhores práticas para implementar o Padrão assíncrono baseado em evento.
Suporte opcional da propriedade IsBusy
Se sua classe não oferecer suporte a várias invocações simultâneas, considere a exposição de uma propriedade IsBusy
. Isso permite que os desenvolvedores determinem se um método MethodNameAsync está sendo executado sem capturar uma exceção do método MethodNameAsync.
Obedeça à semântica IsBusy
descrita em Melhores práticas para implementar o Padrão assíncrono baseado em evento.
Fornecimento opcional de suporte para relatórios de progresso
Costuma ser bom relatar o progresso durante uma operação assíncrona. O padrão assíncrono baseado em evento fornece uma orientação para fazer isso.
Opcionalmente, defina um evento que será gerado pela operação assíncrona e invocado no thread apropriado. O objeto ProgressChangedEventArgs transporta um indicador de progresso com valor de inteiro que deve estar entre 0 e 100.
Nomeie esse evento da seguinte maneira:
ProgressChanged
se a classe tiver várias operações assíncronas (ou exista a expectativa de crescimento a fim de incluir várias operações assíncronas em versões futuras);MethodNameProgressChanged se a classe tiver uma única operação assíncrona.
Essa opção de nomenclatura é comparável à feita para o método de cancelamento, conforme descrito na seção Suporte opcional de cancelamento.
Esse evento deve usar a assinatura do delegado ProgressChangedEventHandler e a classe ProgressChangedEventArgs. Como alternativa, se um indicador de progresso mais específico ao domínio puder ser fornecido (por exemplo, bytes lidos e total de bytes de uma operação de download), você deverá definir uma classe derivada de ProgressChangedEventArgs.
Observe que há apenas um evento ProgressChanged
ou MethodNameProgressChanged para a classe, independentemente do número de métodos assíncronos para os quais ela dá suporte. Os clientes devem usar o objeto userState
que é passado para os métodos MethodNameAsync a fim de distinguir entre as atualizações de andamento em várias operações simultâneas.
Pode haver situações em que várias operações dão suporte ao progresso e cada uma retorna um indicador diferente para o progresso. Nesse caso, um único ProgressChanged
evento não é apropriado, e você pode considerar o suporte a vários eventos ProgressChanged
. Nesse caso, use um padrão de nomenclatura de MethodNameProgressChanged para cada método MethodNameAsync.
Obedeça à semântica de relatório de progresso descrita em Melhores práticas para implementar o Padrão assíncrono baseado em evento.
Fornecimento opcional de suporte para retorno de resultados incrementais
Às vezes, uma operação assíncrona pode retornar resultados incrementais antes da conclusão. Há várias opções que podem ser usadas para oferecer suporte a esse cenário. Veja a seguir alguns exemplos.
Classe de operação única
Se sua classe der suporte apenas a uma única operação assíncrona, e essa operação for capaz de retornar resultados incrementais, então:
Estenda o tipo ProgressChangedEventArgs para carregar os dados de resultado incrementais e definir um evento MethodNameProgressChanged com esses dados estendidos.
Gere este evento MethodNameProgressChanged quando houver um resultado incremental ao relatório.
Essa solução aplica-se especificamente a uma classe de operação assíncrona única, pois não há nenhum problema com o mesmo evento ocorrendo para retornar resultados incrementais em "todas as operações", como faz o evento MethodNameProgressChanged.
Classe de várias operações com resultados incrementais homogêneos
Nesse caso, sua classe oferece suporte a vários métodos assíncronos, cada um capaz de retornar resultados incrementais, e todos esses resultados incrementais têm o mesmo tipo de dados.
Siga o modelo descrito acima para classes de operação única, pois a mesma estrutura EventArgs funcionará para todos os resultados incrementais. Defina um evento ProgressChanged
em vez de um evento MethodNameProgressChanged, já que ele se aplica a vários métodos assíncronos.
Classe de várias operações com resultados incrementais heterogêneos
Se sua classe oferece suporte a vários métodos assíncronos, cada um retornando um tipo diferente de dados, você deve:
Separar o relatório de resultado incremental de seu relatório de progresso.
Definir um evento MethodNameProgressChanged separado com o EventArgs apropriado para cada método assíncrono manipular dados de resultados incrementais desse método.
Invocar esse manipulador de eventos no thread apropriado, conforme descrito em Melhores práticas para implementar o Padrão assíncrono baseado em evento.
Tratamento de parâmetros Out e Ref em métodos
Embora o uso de out
e ref
seja, em geral, desencorajado no .NET, estas são as regras quando eles estiverem presentes:
Em um método síncrono MethodName:
Os parâmetros
out
para MethodName não devem fazer parte de MethodNameAsync. Em vez disso, devem fazer parte de MethodNameCompletedEventArgs com o mesmo nome que seu parâmetro equivalente em MethodName (a menos que haja um nome mais apropriado).Os parâmetros
ref
para MethodName devem aparecer como parte de MethodNameAsync e como parte de MethodNameCompletedEventArgs com o mesmo nome que seu parâmetro equivalente em MethodName (a menos que haja um nome mais apropriado).
Por exemplo, considerando que:
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);
Seu método assíncrono e sua classe AsyncCompletedEventArgs teria esta aparência:
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; };
}
Confira também
- ProgressChangedEventArgs
- AsyncCompletedEventArgs
- Como: Implementar um componente compatível com o padrão assíncrono baseado em evento
- Como: Executar uma operação em segundo plano
- Como: Implementar um formulário que usa uma operação em segundo plano
- Decidindo quando implementar o padrão assíncrono baseado em evento
- Práticas recomendadas para a implementação do padrão assíncrono baseado em evento
- Padrão assíncrono baseado em evento (EAP)