2010-04-23 18 views
43

Nella mia applicazione .NET mi sto iscrivendo agli eventi di un'altra classe. L'abbonamento è condizionale. Mi iscrivo agli eventi quando il controllo è visibile e lo abbandono quando diventa invisibile. Tuttavia, in alcune condizioni, non desidero annullare la sottoscrizione dell'evento anche se il controllo non è visibile poiché desidero il risultato di un'operazione che si sta verificando in un thread in background.Come determinare se un evento è già stato sottoscritto

C'è un modo attraverso il quale posso determinare se una classe è già iscritta a quell'evento?

So che possiamo farlo nella classe che solleverà quell'evento controllando l'evento per null, ma come faccio a farlo in una classe che si iscriverà a quell'evento?

+2

controllare questo link http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/d7d8791f-6aef-4fda-ae0e-5eddcb856706/ –

+0

se solo di castrato * chiunque * è sottoscritto:'bool subscribedTo = theEvent! = null' – Mark

risposta

51

La parola chiave event è stata inventata esplicitamente per impedirti di fare ciò che vuoi fare. Limita l'accesso all'oggetto sottostante delegate in modo che nessuno possa interferire direttamente con le sottoscrizioni degli handler di eventi che archivia. Gli eventi sono accessors per un delegato, proprio come una proprietà è un accessor per un campo. Una proprietà consente solo di ottenere e impostare, un evento consente solo aggiungere e rimuovere.

Ciò mantiene il vostro codice sicuro, altro codice può rimuovere solo un gestore di eventi se conosce il metodo del gestore di eventi e l'oggetto di destinazione. Il linguaggio C# mette un ulteriore livello di sicurezza in atto non consentendo di nominare l'oggetto di destinazione.

E WinForms mette a disposizione un ulteriore livello di sicurezza, quindi diventa difficile anche se si utilizza Reflection.Memorizza istanze delegate in un EventHandlerList con un "cookie" segreto come chiave, dovresti conoscere il cookie per scavare l'oggetto fuori dall'elenco.

Bene, non andare lì. E 'banale per risolvere il problema con un po' di codice sul tuo fine:

private bool mSubscribed; 

private void Subscribe(bool enabled) 
{ 
    if (!enabled) textBox1.VisibleChanged -= textBox1_VisibleChanged; 
    else if (!mSubscribed) textBox1.VisibleChanged += textBox1_VisibleChanged; 

    mSubscribed = enabled; 
} 
+4

Ti piacerebbe avere qualche blog, canale 9 video o msdn articoli che parlano della metodologia di progettazione dietro la creazione di * eventi * così difficili da interagire? Forse se capissi il * perché *, e quali siano i meccanismi * previsti * per realizzare alcune (normalmente) banali cose, potrei avere un momento più facile per trovare soluzioni banali ai miei set di problemi. –

+1

Non è difficile interagire con gli eventi. Incasinare gli eventi * di qualcun altro è difficile, questo è scherzare con le parti private. Fai una domanda al riguardo. –

+2

Ok, * perché * sta complicando gli eventi di qualcun altro? –

0

Non riesci a ricordare se ti sei già iscritto? Questo approccio ha funzionato bene per me finora. Anche se hai un sacco di eventi o oggetti, puoi comunque ricordarlo (in un dizionario, per esempio).

D'altra parte, il cambiamento di visibilità è stato, almeno per me, non un buon punto per iscriversi/annullare l'iscrizione. Di solito preferisco la costruzione/Disposed, che sono più chiari di quando cambia la visibilità di volta in volta.

6

Supponendo che non si abbia accesso alle interiora della classe che dichiara l'evento, non si ha modo di farlo direttamente. Gli eventi espongono solo gli operatori += e -=, nient'altro. Avrai bisogno di una bandiera o di qualche altro meccanismo nella tua classe di iscrizione per sapere se sei già iscritto o meno.

1

Riesci a mettere la logica del processo decisionale nel metodo che attiva l'evento? Supponendo che si sta utilizzando WinForms che sarebbe sembrare qualcosa di simile:

if (MyEvent != null && isCriteriaFulfilled) 
{ 
    MyEvent(); 
} 

Dove isCriteriaFulfilled è determinato dalla tua logica visibile/invisibile.

// AGGIORNAMENTI /////

seguito al vostro primo commento sarebbe non avere senso per modificare il comportamento all'interno del vostro gestore di eventi a seconda del valore di this.Visible?

a.Delegate += new Delegate(method1); 
... 
private void method1() 
{ 
    if (this.Visible) 
     // Do Stuff 
} 

O se avete veramente andare con Iscrizione e annullamento:

private Delegate _method1 = null; 
... 
if(this.visible) 
{ 
    if (_method1 == null) 
     _method1 = new Delegate(method1); 
    a.Delegate += _method1; 
} 
else if (_method1 != null) 
{ 
    a.Delegate -= _method1; 
} 
+1

Mi chiedo se 'areCriteriaFulfilled' è meglio grammaticalmente parlando? –

+0

Sto facendo come segue se (questo) visibile { a.Delegato + = nuovo Delegato (metodo1); } altro { a.Delegate - = nuovo Delegato (metodo1); } – Ram

+0

@Ram: risposta aggiornata. –

1

È sufficiente verificare se il controllo è visibile o meno ogni volta che il gestore di eventi viene attivato.

+0

Non voglio farlo perché gli eventi sono attivati ​​a intervalli regolari e voglio usarli solo quando il mio controllo è invisibile. se faccio quello che stai dicendo, sarà un successo per le prestazioni. – Ram

+0

@Ram: perché pensi che sarà un successo per le prestazioni? Hai misurato il cambiamento nelle prestazioni? –

+0

@Phil: Ciao Phil, è un successo per le prestazioni visto che lo sto facendo con più forme e più eventi. Ogni modulo elabora i dati diff via. Quindi, per evitare l'elaborazione dei dati, sottoscrivo solo gli eventi, la forma è visibile. Credo che l'uso di booleano sarebbe una buona opzione. – Ram

2
/// <summary> 
    /// Determine if a control has the event visible subscribed to 
    /// </summary> 
    /// <param name="controlObject">The control to look for the VisibleChanged event</param> 
    /// <returns>True if the control is subscribed to a VisibleChanged event, False otherwise</returns> 
    private bool IsSubscribed(Control controlObject) 
    { 
    FieldInfo event_visible_field_info = typeof(Control).GetField("EventVisible", 
     BindingFlags.Static | BindingFlags.NonPublic); 
    object object_value = event_visible_field_info.GetValue(controlObject); 
    PropertyInfo events_property_info = controlObject.GetType().GetProperty("Events", 
     BindingFlags.NonPublic | BindingFlags.Instance); 
    EventHandlerList event_list = (EventHandlerList)events_property_info.GetValue(controlObject, null); 
    return (event_list[object_value] != null); 
    } 
0

Sto solo ampliando risposta Hans'. Sto solo cercando di assicurarmi che non stia installando il mio gestore più di una volta e non lo rimuoverò quando ne avrò ancora bisogno. Questo non protegge da un malintenzionato o mal funzionante che si disconnette ripetutamente, per questo è necessario rintracciare i chiamanti, e questo ti aprirebbe semplicemente fino ad avere ripetuti abbonamenti che superano il meccanismo di tracciamento.

// Tracks how many times the ReflectionOnlyResolveHandler has been requested. 
private static int _subscribers = 0; 

/// <summary> 
/// Register or unregister the ReflectionOnlyResolveHandler. 
/// </summary> 
/// <param name="enable"></param> 
public static void SubscribeReflectionOnlyResolve(bool enable) 
{ 
    lock(_lock) 
    { 
     if (_subscribers > 0 && !enable) _subscribers -= 1; 
     else if (enable) _subscribers += 1; 

     if (enable && _subscribers == 1) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += ReflectionHelper.ReflectionOnlyResolveHandler; 
     else if (_subscribers == 0) 
      AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= ReflectionHelper.ReflectionOnlyResolveHandler; 
    } 
} 
+0

I chiamanti di cui sopra non sono realmente "abbonati", stanno solo chiedendo alla classe helper di registrare il suo gestore. –

Problemi correlati