2009-12-10 11 views
6

Quando si dispone di istanze diverse della stessa classe come 'FootballTeam' ad esempio e si desidera consentire a un'altra istanza di questa classe di sapere che si è verificato qualcosa, qual è il modo migliore per farlo?C# class instance communication

Eventi non funziona davvero immagino ...

EG:

FootballTeam A = new FootballTeam(); 
FootballTeam B = new FootballTeam(); 

// now A needs to let B know about it's manager change 
// manager is a property inside this class... 
+0

Si sta tentando di informare tutte le istanze di una classe che qualcosa è successo o un'istanza specifica? – JasonTrue

+0

solo istanze specifiche ... a volte più di una –

risposta

4

Eventi potrebbe funzionare:

FootballTeam A = new FootballTeam(); 
FootballTeam B = new FootballTeam(); 
A.ManagerChanged += B.OnOtherManagerChanged; 

Definizione della manifestazione - FootballTeam chiama il suo metodo OnManagerChanged quando il suo Manager proprietà value modifiche:

class FootballTeam 
{ 
    public event EventHandler ManagerChanged; 

    protected virtual void OnManagerChanged(EventArgs e) 
    { 
     EventHandler handler = ManagerChanged; 
     if (handler != null) 
      handler(this, e); 
    } 

    public void OnOtherManagerChanged(object sender, EventArgs e) 
    { 
     FootballTeam otherTeam = (FootballTeam) sender; 
     // A manager changed on a different FootballTeam instance 
     // ...do something here 
    } 
} 
0

È possibile utilizzare il Observer pattern. Permette agli oggetti di "iscriversi" agli eventi che gli interessano.

1

Il modo tipico di fare questo in NET è per definire un evento per operazioni che sono interessanti per altri oggetti. Per esempio si potrebbe definire i seguenti

public class FootballTeam { 
    private string _manager; 
    public string Manager { 
    get { return _manager; } 
    set { 
     if (ManagerChanged != null) { 
     ManagerChanged(this,EventArgs.Empty); 
     } 
    } 
    } 
    public event EventHandler ManagerChanged; 
} 

Idealmente si vorrebbe un tipo di evento più sicuri, ma c'è solo così tanto spazio qui

È quindi possibile ascoltare questo evento in altri casi FootballTeam e rispondere a quella evento.

FootballTeam a = new FootballTeam(); 
FootballTeam b = new FootballTeam(); 
a.ManagerChanged += (sender, e) => { 
    Console.WriteLine("A's manager changed"); 
}; 
1

Ci sono diversi modi. Il fatto che sia necessario farlo a volte è indicativo di un problema di progettazione, anche se ovviamente il pragmatismo deve entrare in gioco.

Un modo semplice è quello di avere un evento di private static nella classe FootballTeam che si sottoscrive nel ctor:

public class FootballTeam 
{ 
    private static event EventHandler SomethingHappened; 

    public FootballTeam() 
    { 
     SomethingHappened += this.HandleSomethingHappened; 
    } 

    public void DoSomething() 
    { 
     SomethingHappened(); //notifies all instances - including this one! 
    } 
} 

Per evitare una perdita di memoria, assicurarsi di pulire i gestori di eventi mediante l'attuazione IDisposable :

public class FootballTeam : IDisposable 
{ 
    //... 

    public void Dispose() 
    { 
     SomethingHappened -= this.HandleSomethingHappened; 
     //release the reference to this instance so it can be GC'd 
    } 
} 
+0

un'altra domanda, perché la menzioni qui, si deve sempre smaltire manualmente gli eventatori usando IDisposable? –

+1

No - è importante solo in questo caso perché l'evento è statico, quindi l'evento è permanente e conterrà un riferimento all'istanza a cui è stato collegato un gestore. Quindi l'istanza non otterrà mai GC, perché il GC vede che c'è ancora un riferimento attivo ad esso. Rimuoviamo il riferimento quando abbiamo finito per evitare quel problema. –

+0

quindi con eventi normali, non è necessario implementare in modo esplicito IDisposable e sganciare i gestori di eventi? –

4

ci sono un sacco di risposte a questa domanda che forniscono esempi di come risolvere questo tipo di problema utilizzando gli eventi (o il pattern Observer). E mentre questi sono schemi appropriati da impiegare, scegliere come e quando usarli può avere un impatto significativo sul risultato finale.

Nel tuo esempio, descrivi le istanze FootballTeam che devono essere informate quando si verificano cambiamenti rilevanti in altre istanze. Tuttavia, è necessario decidere se la competenza della classe deve essere effettivamente utilizzata per ascoltare e rispondere a tali eventi. Lo Single Responsibility Principle afferma che dovrebbe esserci un solo motivo per cambiare una classe. Aderendo a questo concetto di design generalmente si ottiene una separazione più chiara delle preoccupazioni e un codice complessivo migliore.

Ci sono una serie di problemi che possono sorgere in un tale progetto.

Innanzitutto, avere ciascuna FootballTeam responsabile di ascoltare le modifiche e rispondere a esse può rapidamente diventare problematico. Per prima cosa, il numero di linee di comunicazione dirette (punto-punto) tra istanze cresce con il quadrato del numero di istanze: n * (n-1). Con cinque squadre avresti 20 connessioni, con dieci squadre ne avresti 90, con trenta squadre ne avresti 870.

La gestione delle sottoscrizioni di eventi (e di annullamento della sottoscrizione) per tutte queste istanze può generare codice confuso e non gestibile . Inoltre, il fatto di avere sottoscrizioni di eventi tra ogni coppia di team può influire sulla raccolta dei dati inutili e determinare potenziali perdite o, per lo meno, le istanze degli oggetti che rimangono nella memoria molto più a lungo del necessario.

Un altro potenziale problema con un progetto in cui tutte le istanze si iscrivono agli eventi, sono catene di eventi infiniti o ciclici: A notifica B, che si aggiorna e notifica C, che si aggiorna e notifica A, che si aggiorna e notifica B ... all'infinito. O finché non esaurisci lo spazio disponibile. Questo può essere un problema difficile da risolvere in un tale progetto e probabilmente richiederebbe una gestione dello stato scomoda per prevenire cicli e ricorsione.

Un approccio alternativo, è quello di creare una classe osservatore separato - chiamiamola FootballTeamObserver - che sottoscrive gli eventi di modifica di tutte le istanze FootballTeam, e sarebbe responsabile della propagazione delle modifiche, se necessario, attraverso le istanze. Quindi FootballTeam sarebbe ancora responsabile della trasmissione quando si verifica un cambiamento significativo e FootballTeamObserver risponderebbe alla notifica. È importante sottolineare che ci sarà sempre una sola istanza di FootballTeamObserver - the singleton pattern - garantendo che esista un sito di gestione centrale per tutte le notifiche. Questo riduce sia il numero di abbonamenti di eventi (che sarebbero tanti quanti quanti ce ne sono) che separa la responsabilità di rispondere ai cambiamenti in modo pulito. Può anche essere responsabile del rilevamento del ciclo e assicurarsi che le catene di aggiornamento abbiano una lunghezza finita.

+0

+1 buona spiegazione – Simon

+0

Qualsiasi codice o collegamento di esempio per questo? – Ujjwal