CodeGym /Corsi /C# SELF /Raccomandazioni per l'organizzazione del codice

Raccomandazioni per l'organizzazione del codice

C# SELF
Livello 54 , Lezione 1
Disponibile

1. Stile di dichiarazione e denominazione degli eventi

Gli eventi non sono semplici delegate. Sono un'entità separata per la comunicazione tra parti dell'applicazione, e la loro dichiarazione deve essere chiara.

Usa il tipo di delegate corretto

Nel 99% dei casi usa i delegate standard:

  • EventHandler — per eventi senza dati.
  • EventHandler<TEventArgs> — quando serve passare parametri.

La standardizzazione facilita la manutenzione del codice e l'integrazione con le librerie .NET. Non inventare un delegate custom se va bene EventHandler.


public event EventHandler SomethingHappened; // Nessun dato
public event EventHandler<MyEventArgs> DataReceived; // Ci sono dati aggiuntivi

Se serve una personalizzazione particolare — dichiara un delegate tuo, ma è raro.

Denominazione degli eventi

In .NET gli eventi si chiamano al passato: Completed, Clicked, Changed, Received. Questo sottolinea il fatto che è successo qualcosa.

Esempi:


public event EventHandler DataLoaded;    // I dati sono stati caricati
public event EventHandler<MessageEventArgs> MessageReceived; // Messaggio ricevuto
public event EventHandler Saving;        // Il processo di salvataggio è iniziato

A volte si usa la forma Changing per eventi "prima" della modifica, per dare la possibilità di intervenire.

2. Organizzazione della classe publisher: metodo virtuale OnEvent

Aggiungi sempre un metodo protected virtual che invoca l'evento: punto centrale di invocazione, estendibilità tramite ereditarietà e comportamento prevedibile.


public class FileLoader
{
    public event EventHandler<FileLoadedEventArgs> FileLoaded;

    protected virtual void OnFileLoaded(FileLoadedEventArgs e)
    {
        FileLoaded?.Invoke(this, e);
    }

    public void Load(string filename)
    {
        // ... logica di caricamento file ...
        OnFileLoaded(new FileLoadedEventArgs(filename));
    }
}

public class FileLoadedEventArgs : EventArgs
{
    public string FileName { get; }
    public FileLoadedEventArgs(string fileName) => FileName = fileName;
}

Fai in modo che solo OnFileLoaded invochi l'evento — così è più semplice mantenere e testare.

3. Regole di sottoscrizione e desottoscrizione: ciclo di vita, IDisposable

Se la vita del subscriber è più breve di quella del publisher, assicurati di desottoscriverti prima della distruzione del subscriber. È comodo implementare IDisposable e desottoscrivere in Dispose().


public class TemporaryListener : IDisposable
{
    private readonly Publisher _publisher;

    public TemporaryListener(Publisher publisher)
    {
        _publisher = publisher;
        _publisher.DataReceived += HandleData;
    }

    private void HandleData(object sender, EventArgs e)
    {
        // Lavoro con i dati
    }

    public void Dispose()
    {
        _publisher.DataReceived -= HandleData;
    }
}

// Uso con using:
using (var listener = new TemporaryListener(myPublisher))
{
    // listener ascolta gli eventi qui
}
// Dopo l'uscita dal using - Dispose è stato chiamato, avviene la desottoscrizione

Se dimentichi di desottoscriverti, il publisher terrà il riferimento al delegate del subscriber — avrai memory leak e "oggetti zombi".

4. Invocazione thread-safe degli eventi

Nel codice multithread i subscriber possono essere aggiunti/rimossi in parallelo all'invocazione dell'evento. Questo può causare race e NullReferenceException. Usa il pattern thread-safe: copia il delegate in una variabile locale.


protected virtual void OnSomethingHappened()
{
    EventHandler handler = SomethingHappened;
    handler?.Invoke(this, EventArgs.Empty);
}

Con C# 6+ è sufficiente:


SomethingHappened?.Invoke(this, EventArgs.Empty);

5. Usa EventArgs invece di object

Non passare dati tramite object o campi di classe. Usa la tipizzazione forte tramite classi derivate da EventArgs.


public class DownloadCompletedEventArgs : EventArgs
{
    public string FileName { get; }
    public long Size { get; }
    public DownloadCompletedEventArgs(string fileName, long size)
    {
        FileName = fileName;
        Size = size;
    }
}

public event EventHandler<DownloadCompletedEventArgs> DownloadCompleted;

6. Documentare eventi e subscriber

Documenta: quando viene invocato l'evento, il significato dei campi di EventArgs, se è necessario desottoscriversi e quando.


/// <summary>
/// L'evento scatta dopo il successo del caricamento dei dati.
/// </summary>
public event EventHandler<DataLoadedEventArgs> DataLoaded;

7. Raccomandazioni architetturali sugli eventi

Separa le responsabilità

Il publisher deve solo notificare il fatto. Il subscriber decide autonomamente quando sottoscriversi e desottoscriversi.

Evita di "sparare" eventi in continuazione

Non generare lo stesso evento decine di volte al secondo senza bisogno — è un carico superfluo.

Evita usare eventi per comunicazione bidirezionale

Gli eventi sono per lo schema "uno notifica — molti ascoltano". Per comunicazione bidirezionale considera interfacce, callback o altri meccanismi.

Non mantenere riferimenti espliciti ai subscriber nella classe

Non tenere riferimenti espliciti ai subscriber — eventi e delegate lo fanno automaticamente.

8. Anti-pattern classici

Eventi non tipizzati


public event Action<object> SomethingHappened; // Non è chiaro cosa c'è dentro

Male: viene spezzata la tipizzazione, servono cast, si perde manutenibilità.

Dimenticare la desottoscrizione


public class ShortLivedListener
{
    public ShortLivedListener(Publisher p) =>
        p.DataReceived += DoWork;

    private void DoWork(object sender, EventArgs e) { /* ... */ }
    // Nessun Dispose, nessuna desottoscrizione => oggetti zombi!
}

Violazione di SRP

La classe è contemporaneamente publisher, subscriber e handler — mescolanza di ruoli. Separa le responsabilità.

9. Applicazioni pratiche in colloqui e progetti

In molti progetti basati su publish-subscribe una buona organizzazione degli eventi è fondamentale per scalabilità e manutenzione. Nei colloqui spesso chiedono di:

  • implementare un sistema di eventi con corretta tipizzazione,
  • mostrare la gestione del ciclo di vita dei subscriber,
  • spiegare l'invocazione thread-safe degli eventi.

Codice di eventi pulito, documentato e ben organizzato ti fa subito distinguere tra i candidati.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION