Como publicar eventos em conformidade com as diretrizes do .NET (Guia de Programação em C#)

O procedimento a seguir demonstra como adicionar eventos que seguem o padrão .NET para classes e structs. Todos os eventos na biblioteca de classes do .NET se baseiam no delegado EventHandler, que é definido da seguinte maneira:

public delegate void EventHandler(object? sender, EventArgs e);

Observação

O .NET Framework 2.0 apresenta uma versão genérica desse delegado, o EventHandler<TEventArgs>. Os exemplos a seguir mostram como usar as duas versões.

Embora os eventos nas classes que você define possam ser baseados em qualquer tipo delegado válido, até mesmo nos delegados que retornam um valor, é geralmente recomendável que você baseie seus eventos no padrão .NET usando o EventHandler, conforme mostrado no exemplo a seguir.

O nome EventHandler pode levar a um pouco de confusão, pois ele, na verdade, não lida com o evento. Os tipos EventHandler e os tipos EventHandler<TEventArgs> genéricos são delegados. Um método ou expressão lambda cuja assinatura corresponda à definição do delegado é o manipulador de eventos e será invocada quando o evento for gerado.

Publicar eventos com base no padrão EventHandler

  1. (Ignore esta etapa e vá para a 3ª Etapa se você não precisar enviar dados personalizados com o evento.) Declare a classe para seus dados personalizados em um escopo visível para suas classes de publicador e assinante. Em seguida, adicione os membros necessários para manter seus dados de evento personalizados. Neste exemplo, uma cadeia de caracteres simples é retornada.

    public class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(string message)
        {
            Message = message;
        }
    
        public string Message { get; set; }
    }
    
  2. (Ignore esta etapa se você estiver usando a versão genérica do EventHandler<TEventArgs>.) Declare um delegado em sua classe de publicação. Dê um nome que termine com EventHandler. O segundo parâmetro especifica o tipo personalizado EventArgs.

    public delegate void CustomEventHandler(object? sender, CustomEventArgs args);
    
  3. Declare o evento em sua classe de publicação, usando uma das etapas a seguir.

    1. Se você não tiver uma classe EventArgs personalizada, o tipo de evento será o delegado EventHandler não genérico. Você não precisa declarar o delegado porque ele já está declarado no namespace System que está incluído quando você cria seu projeto do C#. Adicione o seguinte código à sua classe publicadora.

      public event EventHandler? RaiseCustomEvent;
      
    2. Se você estiver usando a versão não genérica de EventHandler e você tem uma classe personalizada derivada de EventArgs, declare o evento dentro de sua classe de publicação e use o delegado da etapa 2 como o tipo.

      public event CustomEventHandler? RaiseCustomEvent;
      
    3. Se você estiver usando a versão genérica, não é necessário um delegado personalizado. Em vez disso, na sua classe de publicação, especifique o tipo de evento como EventHandler<CustomEventArgs>, substituindo o nome da sua própria classe entre os colchetes angulares.

      public event EventHandler<CustomEventArgs>? RaiseCustomEvent;
      

Exemplo

O exemplo a seguir demonstra as etapas anteriores, usando uma classe EventArgs personalizada e o EventHandler<TEventArgs> como o tipo de evento.

namespace DotNetEvents
{
    // Define a class to hold custom event info
    public class CustomEventArgs : EventArgs
    {
        public CustomEventArgs(string message)
        {
            Message = message;
        }

        public string Message { get; set; }
    }

    // Class that publishes an event
    class Publisher
    {
        // Declare the event using EventHandler<T>
        public event EventHandler<CustomEventArgs>? RaiseCustomEvent;

        public void DoSomething()
        {
            // Write some code that does something useful here
            // then raise the event. You can also raise an event
            // before you execute a block of code.
            OnRaiseCustomEvent(new CustomEventArgs("Event triggered"));
        }

        // Wrap event invocations inside a protected virtual method
        // to allow derived classes to override the event invocation behavior
        protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
        {
            // Make a temporary copy of the event to avoid possibility of
            // a race condition if the last subscriber unsubscribes
            // immediately after the null check and before the event is raised.
            EventHandler<CustomEventArgs>? raiseEvent = RaiseCustomEvent;

            // Event will be null if there are no subscribers
            if (raiseEvent != null)
            {
                // Format the string to send inside the CustomEventArgs parameter
                e.Message += $" at {DateTime.Now}";

                // Call to raise the event.
                raiseEvent(this, e);
            }
        }
    }

    //Class that subscribes to an event
    class Subscriber
    {
        private readonly string _id;

        public Subscriber(string id, Publisher pub)
        {
            _id = id;

            // Subscribe to the event
            pub.RaiseCustomEvent += HandleCustomEvent;
        }

        // Define what actions to take when the event is raised.
        void HandleCustomEvent(object? sender, CustomEventArgs e)
        {
            Console.WriteLine($"{_id} received this message: {e.Message}");
        }
    }

    class Program
    {
        static void Main()
        {
            var pub = new Publisher();
            var sub1 = new Subscriber("sub1", pub);
            var sub2 = new Subscriber("sub2", pub);

            // Call the method that raises the event.
            pub.DoSomething();

            // Keep the console window open
            Console.WriteLine("Press any key to continue...");
            Console.ReadLine();
        }
    }
}

Confira também