INTRODUZIONE AGLI EVENTI

C sharpPrima di introdurre nel codice la gestione degli eventi in c sharp, cerchiamo di chiarire con una figura il modello utilizzato per notificare un qualche cambiamento, può essere la notifica di una proprietà che è cambiata, ma lo stesso modello si applica anche ad elementi appartenenti ad una GUI (Graphic User Interface) come il click su un pulsante o la selezione di un item da una Listbox etc.

Schema Eventi

GLI EVENTI IN C SHARP, LA DEFINIZIONE DI UN EVENTO

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}");
        }
    }

Riprendiamo le classi che gestiscono un conto corrente bancario, vogliamo che quando cambia il saldo di un utente questa variazione venga notificata alla classe Program. In uno scenario reale la banca in risposta all’evento genera un SMS da inviare al cliente con il nuovo saldo e i dettagli dell’operazione. La classe Utente sarà il nostro Emitter. Come vedi un evento si dichiara con la parola chiave event e la dichiarazione di un evento corrisponde ad un delegate di tipo Action(T arg) nel nostro caso in quanto dobbiamo notificare solo la variazione di una property, il saldo, l’argomento T sarà di tipo decimal.

public event Action<decimal> NewSaldo;

Una volta dichiarato l’evento si crea un metodo che inizia con il suffisso On seguito dal nome dell’evento quindi OnNewSaldo avente una signature compatibile con quella del delegate. Questo metodo deve essere invocato quando cambia la property saldo, e quindi nel setter andiamo a richiamare OnNewSaldo passando come argomento il nuovo saldo. In questo metodo si deve controllare come prima cosa che ci siano dei Listner attivi e pronti a ricevere l’evento e ciò lo si fa controllando che NewSaldo sia diverso da null. Se l’evento ha dei sottoscrittori attivi si emette l’evento a tutti i Listner registrati.

GLI EVENTI IN C SHARP, AGGIUNGERE UN LISTENER

Una volta definito un Emitter dobbiamo fare in modo che ci sia almeno un Listener pronto a ricevere l’evento. Riporto la classe Program.

 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}");
        }
    }

Come vedi ho aggiunto un metodo alla classe avente una signature compatibile con quella del delegate. SaldoHandler gestisce la risposta all’evento generato dall’emitter ossia dalla classe Utente. Infine bisogna registrare il metodo affinché possa ricevere l’evento generato dall’emitter. Ti riporto la riga di codice.

utente.NewSaldo += Program.SaldoHandler;

Sotto il codice completo dell’esempio.

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;
        }
    }
}

LINK AI PRECEDENTI POST

LINK AL CODICE SU GITHUB