Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O artigo anterior discutia os padrões de evento mais comuns. O .NET Core tem um padrão mais relaxado. Nesta versão, a EventHandler<TEventArgs> definição não tem mais a restrição que TEventArgs deve ser uma classe derivada de System.EventArgs.
Isso aumenta a flexibilidade para você e é compatível com versões anteriores. Vamos começar com a flexibilidade. A implementação para System.EventArgs usa um método definido em System.Object, que é: MemberwiseClone(), e cria uma cópia superficial do objeto. Esse método deve usar a reflexão para implementar sua funcionalidade para qualquer classe derivada de EventArgs. Essa funcionalidade é mais fácil de criar em uma classe derivada específica. Isso significa efetivamente que derivar de System.EventArgs impõe uma restrição que limita seus designs, mas não oferece nenhum benefício adicional. Na verdade, você pode alterar as definições de FileFoundArgs e SearchDirectoryArgs para que elas não derivam de EventArgs. O programa funciona exatamente da mesma forma.
Você também pode alterar o SearchDirectoryArgs para um struct se fizer mais uma alteração:
internal struct SearchDirectoryArgs
{
internal string CurrentSearchDirectory { get; }
internal int TotalDirs { get; }
internal int CompletedDirs { get; }
internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
{
CurrentSearchDirectory = dir;
TotalDirs = totalDirs;
CompletedDirs = completedDirs;
}
}
A alteração extra é chamar o construtor sem parâmetros antes de entrar no construtor que inicializa todos os campos. Sem essa adição, as regras de C# relatariam que as propriedades estão sendo acessadas antes de serem atribuídas.
Você não deve alterar a FileFoundArgs de uma classe (tipo de referência) para um struct (tipo de valor). O protocolo para lidar com o cancelamento requer que você passe argumentos de eventos por referência. Se você fizesse a mesma alteração, a classe de pesquisa de arquivo nunca observaria as alterações feitas por qualquer um dos assinantes do evento. Uma nova cópia da estrutura seria usada para cada assinante e essa cópia seria uma cópia diferente daquela vista pelo objeto de pesquisa de arquivo.
Em seguida, vamos considerar como essa alteração pode ser compatível com versões anteriores. A remoção da restrição não afeta nenhum código existente. Qualquer tipo de argumento de evento existente ainda deriva de System.EventArgs. A compatibilidade com versões anteriores é uma das principais razões pelas quais elas continuam derivando de System.EventArgs. Todos os assinantes de eventos existentes são assinantes de um evento que seguiu o padrão clássico.
Seguindo uma lógica semelhante, qualquer tipo de argumento de evento criado agora não teria assinantes em nenhuma base de código existente. Novos tipos de evento que não derivam de System.EventArgs não quebram essas bases de código.
Eventos com assinantes assíncronos
Você tem um último padrão para aprender: como escrever corretamente os assinantes do evento que chamam o código assíncrono. O desafio é descrito no artigo em async e await. Métodos assíncronos podem ter um tipo de retorno nulo, mas isso não é recomendável. Quando o código do assinante de evento chama um método assíncrono, você não tem escolha a não ser criar um método async void. A assinatura do manipulador de eventos requer isso.
Você precisa reconciliar essa orientação oposta. De alguma forma, você deve criar um método seguro async void . As noções básicas do padrão que você precisa implementar são mostradas no seguinte código:
worker.StartWorking += async (sender, eventArgs) =>
{
try
{
await DoWorkAsync();
}
catch (Exception e)
{
//Some form of logging.
Console.WriteLine($"Async task failure: {e.ToString()}");
// Consider gracefully, and quickly exiting.
}
};
Primeiro, observe que o manipulador está marcado como um manipulador assíncrono. Como está sendo atribuído a um tipo de delegado de manipulador de eventos, ele tem um tipo de retorno nulo. Isso significa que você deve seguir o padrão mostrado no manipulador e não permitir que exceções sejam descartadas do contexto do manipulador assíncrono. Como ele não retorna uma tarefa, não há nenhuma tarefa que pode relatar o erro entrando no estado de falha. Como o método é assíncrono, o método não pode gerar a exceção. (O método de chamada continua a execução porque é async.) O comportamento real do runtime é definido de forma diferente para ambientes diferentes. Ele pode encerrar o thread ou o processo que possui o thread ou deixar o processo em um estado indeterminado. Todos esses resultados potenciais são altamente indesejáveis.
Você deve encapsular a expressão await para a tarefa assíncrona em seu próprio bloco try. Se isso causar uma tarefa com falha, você pode registrar o erro em log. Se for um erro do qual seu aplicativo não pode se recuperar, você poderá sair do programa rapidamente e normalmente
Este artigo explicou as principais atualizações para o padrão de evento .NET. Você pode ver muitos exemplos das versões anteriores nas bibliotecas com as quais trabalha. No entanto, você também deve entender quais são os padrões mais recentes. Você pode ver o código concluído para o exemplo em Program.cs.
O próximo artigo desta série ajuda você a distinguir entre usar delegates e events em seus designs. São conceitos semelhantes e esse artigo ajuda você a tomar a melhor decisão para seus programas.