INTRODUCTION TO THE EVENTS

C sharpBefore introducing in the code the management of events in c sharp, let’s try to clarify with a figure the model used to notify some change, it can be the notification of a property that has changed, but the same model also applies to elements belonging to a GUI (Graphic User Interface) such as clicking on a button or selecting an item from a ListBox etc.

Scheme

THE EVENTS IN C SHARP, THE DEFINITION OF AN EVENT

internal class Utente
    {
        private decimal saldo;
        
        public string Id { get; set; }
        public string Nome { get; set; }
        public string Cognome { get; set; }

        public decimal Saldo
        {
            get => saldo;
            set
            {
                if (value < 0)
                {
                    Console.WriteLine($"Impossibile impostare il saldo. Valore passato: {value}");
                }
                else
                {
                    saldo = value;
                    OnNewSaldo(value); //SE VIENE CAMBIATO IL SALDO EMETTO L'EVENTO SE CI SONO
                    //DEI LISTNER ATTIVI.
                }
            }
        }

        public string NumeroConto { get; set; }

        public string Denominazione => "Utente " + Nome + " " + Cognome;

        public event Action<decimal> NewSaldo; //DEFINIZIONE DI UN EVENTO

        public void OnNewSaldo(decimal saldo)
        {
            if (NewSaldo != null) //SE NON CI SONO ASCOLTATORI REGISTRATI NewSaldo VALE NULL.
                NewSaldo(saldo); //EMETTO L'EVENTO A TUTTI I SOTTOSCRITTORI.
        }

        internal void SaldoCorrente()
        {
            Console.WriteLine($"Utente {Nome + " " + Cognome} saldo: {Saldo}");
        }
    }

Let’s go back to the classes that manage a bank account, we want that when a user’s balance changes this variation is notified to the Program class. In a real scenario, the bank in response to the event generates an SMS to be sent to the customer with the new balance and the details of the operation. The User class will be our Emitter. As you can see, an event is declared with the keyword event and the declaration of an event corresponds to a delegate of type Action (T arg) in our case as we only have to notify the variation of a property, the balance, the argument T will be of type decimal.

public event Action<decimal> NewSaldo;

Once the event is declared, a method is created that begins with the suffix On followed by the name of the event and then OnNewSaldo having a signature compatible with that of the delegate. This method must be invoked when the property balance changes, and then in the setter we call OnNewSaldo passing the new balance as an argument. In this method, you must first check that there are active Listners ready to receive the event and this is done by checking that NewSaldo is different from null. If the event has active subscribers, the event is issued to all registered listners.

EVENTS IN C SHARP, ADD A LISTENER

Once an Emitter has been defined, we must ensure that there is at least one Listener ready to receive the event. I’m carrying over the Program class.

internal class Program
    {
        private static Utente utente;
        private static Banca banca;

        private static void Main(string[] args)
        {
            utente = new() { Id = "X001", Cognome = "Rossi", Nome = "Mario", NumeroConto = "19854378" };
            var p = new Program();
            utente.NewSaldo += p.SaldoHandler; //SOTTOSCRIZIONE EVENTO
            utente.Saldo = 13000m;
            utente.SaldoCorrente();
            banca = new Banca("1234567890", "UBI Banca");
            banca.Preleva(8000m,utente);
            utente.SaldoCorrente();
        }

        /*--GESTIONE DELL'EVENTO NewSaldo. IL METODO DEVE AVERE UNA SIGNATURE
        COMPATIBILE CON IL DELEGATE ACTION DEFINITO NELLA CLASSE UTENTE.*/
        public void SaldoHandler(decimal saldo)
        {
            Console.WriteLine($"Saldo utente {utente.Denominazione} è cambiato. Nuovo valore: {utente.Saldo}");
        }
    }

As you can see I added a method to the class having a signature compatible with that of the delegate. SaldoHandler manages the response to the event generated by the Emitter or by the Utente class. Finally, the method must be registered so that it can receive the event generated by the emitter. I’ll bring you the line of code.

utente.NewSaldo += Program.SaldoHandler.

Below the complete code of the example.

using System;

namespace Eventi
{
    internal class Program
    {
        private static Utente utente;
        private static Banca banca;

        private static void Main(string[] args)
        {
            utente = new() { Id = "X001", Cognome = "Rossi", Nome = "Mario", NumeroConto = "19854378" };
            var p = new Program();
            utente.NewSaldo += p.SaldoHandler; //SOTTOSCRIZIONE EVENTO
            utente.Saldo = 13000m;
            utente.SaldoCorrente();
            banca = new Banca("1234567890", "UBI Banca");
            banca.Preleva(8000m,utente);
            utente.SaldoCorrente();
        }

        /*--GESTIONE DELL'EVENTO NewSaldo. IL METODO DEVE AVERE UNA SIGNATURE
        COMPATIBILE CON IL DELEGATE ACTION DEFINITO NELLA CLASSE UTENTE.*/
        public void SaldoHandler(decimal saldo)
        {
            Console.WriteLine($"Saldo utente {utente.Denominazione} è cambiato. Nuovo valore: {utente.Saldo}");
        }
    }

    internal class Utente
    {
        private decimal saldo;
        
        public string Id { get; set; }
        public string Nome { get; set; }
        public string Cognome { get; set; }

        public decimal Saldo
        {
            get => saldo;
            set
            {
                if (value < 0)
                {
                    Console.WriteLine($"Impossibile impostare il saldo. Valore passato: {value}");
                }
                else
                {
                    saldo = value;
                    OnNewSaldo(value); //SE VIENE CAMBIATO IL SALDO EMETTO L'EVENTO SE CI SONO
                    //DEI LISTNER ATTIVI.
                }
            }
        }

        public string NumeroConto { get; set; }

        public string Denominazione => "Utente " + Nome + " " + Cognome;

        public event Action<decimal> NewSaldo; //DEFINIZIONE DI UN EVENTO

        public void OnNewSaldo(decimal saldo)
        {
            if (NewSaldo != null) //SE NON CI SONO ASCOLTATORI REGISTRATI NewSaldo VALE NULL.
                NewSaldo(saldo); //EMETTO L'EVENTO A TUTTI I SOTTOSCRITTORI.
        }

        internal void SaldoCorrente()
        {
            Console.WriteLine($"Utente {Nome + " " + Cognome} saldo: {Saldo}");
        }
    }

    internal class Banca
    {
        public Banca(string id, string denominazione, string abi, string cab)
        {
            Id = id;
            Denominazione = denominazione;
            Abi = abi;
            Cab = cab;
        }

        public Banca(string id, string denominazione) :
            this(id, denominazione, "06098", "14400")
        {
        }

        public string Id { get; set; }
        public string Denominazione { get; set; }
        public string Abi { get; set; }
        public string Cab { get; set; }
        public Utente Utente { get; set; }

        internal void Preleva(decimal importo, Utente utente)
        {
            this.Utente = utente;
            var saldo = Utente.Saldo;
            if (importo > saldo)
            {
                Console.WriteLine($"Operazione di prelievo non ammessa, importo richiesto {importo}");
                return;
            }

            if (importo < 0)
            {
                Console.WriteLine($"Importo prelevato non valido: {importo}");
                return;
            }

            Utente.Saldo -= importo;
            this.Utente = null;
        }

        internal void Accredito(decimal importo, Utente utente)
        {
            this.Utente = utente;
            if (importo < 0)
            {
                Console.WriteLine($"Importo accreditato non valido: {importo}");
                return;
            }

            Utente.Saldo += importo;
            this.Utente = null;
        }
    }
}

LINKS TO PREVIOUS POST

LINK TO THE CODE ON GITHUB