Csharp-Using Events

2 minute read

Let’s understund how events works in c#.

Delegates VS Events

An event is a pair of add/remove methods (like a property with a get and a set).

Assignment and invocation (=) is private to the containing class. Subscription (-= , +=) is allowed from outside the containing class.

With a public delegate field, anyone can remove other people’s (using assignment =) event handlers, raise the event themselves.

The Publisher-Subscriber (pub-sub) pattern is an implementation of event-driven architecture.

In the Observer pattern, the Observers are aware of the Subscribers. In Publisher/Subscriber, publishers and subscribers don’t need to know each other.

public delegate void NotifyHandler();

    public class SampleEventClient
    {
        void Program() {

            // Use case: Multicast Delegates
            NotifyHandler n = () => Console.WriteLine("Server 1 Notified");
            n += () => Console.WriteLine("Server 2 Notified");
            n.Invoke();

            //n = null; //  Assignment and invocation are public = issues

            // Use case: Delegate + Event
            var e = new NotificationEvent();
            e.Notify += () => Console.WriteLine("Server 1 Notified"); // subscription to event is public
            e.Notify += () => Console.WriteLine("Server 2 Notified");
            //e.Notify = null; // compile error, assignment is private to the event containing class.
            //e.Notify?.Invoke(); // compile error, invocation is private to the event containing class.

            e.Raise();


            // Use case: EventHandler
            var eh = new NotificationEvent();

            eh.OnErrorEventHandler += (o, args) => Console.WriteLine($"Log to file: {args.Message}");
            eh.OnErrorEventHandler += (o, args) => Console.WriteLine($"Log to database: {args.Message}");
            //e.OnErrorEventHandler = (o, args) => Console.WriteLine($"Compile error !"); // Compile error


            // Act
            eh.OnError(this, new ErrorEventArgs("Server 1 error"));

        }
    }

    public class NotificationEvent: INotificationEvent
    {
        public event NotifyHandler Notify = null;
        //public event Action<ErrorEventArgs> OnError;

        // Represents the signature: public delegate void EventHandler(object? sender, EventArgs e);
        public event EventHandler<ErrorEventArgs> OnErrorEventHandler = null;

        // Thread safe event
        // readonly object objLock = new object();
        //public event NotifyHandler Notify
        //{
        //
        //    add {
        //      lock (objLock)
        //      {
        //          Notify += value;
        //      }
        //    }
        //    remove
        //    {
        //       lock (objLock)
        //      {
        //          Notify -= value;
        //      }
        //    }
        //}

        public void Raise()
        {
          // Thread safe event
          // Notify handler;
          // lock (objLock)
          // {
          //    handler = Notify;
          // }
          Notify?.Invoke();
        }

        public void OnError(object o, ErrorEventArgs e)
        {
            OnErrorEventHandler?.Invoke(o, e);
        }
    }

    public interface INotificationEvent {
        event NotifyHandler Notify;
        //event Action<ErrorEventArgs> OnError;
        event EventHandler<ErrorEventArgs> OnErrorEventHandler;
    }

    public class ErrorEventArgs : EventArgs
    {
        public string Message { get; }

        public ErrorEventArgs(string message)
        {
            this.Message = message;
        }
    }