Annulla l’iscrizione al metodo anonimo in C #

È ansible annullare l’iscrizione a un metodo anonimo da un evento?

Se mi iscrivo ad un evento come questo:

void MyMethod() { Console.WriteLine("I did it!"); } MyEvent += MyMethod; 

Posso annullare la sottoscrizione in questo modo:

 MyEvent -= MyMethod; 

Ma se mi iscrivo usando un metodo anonimo:

 MyEvent += delegate(){Console.WriteLine("I did it!");}; 

è ansible annullare l’iscrizione a questo metodo anonimo? Se é cosi, come?

     Action myDelegate = delegate(){Console.WriteLine("I did it!");}; MyEvent += myDelegate; // .... later MyEvent -= myDelegate; 

    Basta tenere un riferimento al delegato in giro.

    Una tecnica è dichiarare una variabile per contenere il metodo anonimo che sarebbe quindi disponibile all’interno del metodo anonimo stesso. Questo ha funzionato per me perché il comportamento desiderato era quello di annullare l’iscrizione dopo che l’evento è stato gestito.

    Esempio:

     MyEventHandler foo = null; foo = delegate(object s, MyEventArgs ev) { Console.WriteLine("I did it!"); MyEvent -= foo; }; MyEvent += foo; 

    Dalla memoria, la specifica esplicitamente non garantisce il comportamento in entrambi i casi quando si tratta dell’equivalenza dei delegati creati con metodi anonimi.

    Se è necessario annullare l’iscrizione, è necessario utilizzare un metodo “normale” o conservare il delegato da qualche altra parte in modo da poter annullare l’iscrizione con esattamente lo stesso delegato utilizzato per la sottoscrizione.

    Nel 3.0 può essere abbreviato in:

     MyHandler myDelegate = ()=>Console.WriteLine("I did it!"); MyEvent += myDelegate; ... MyEvent -= myDelegate; 

    Invece di mantenere un riferimento a qualsiasi delegato, puoi strumentare la tua class in modo da restituire l’elenco di invocazione dell’evento al chiamante. Fondamentalmente puoi scrivere qualcosa come questo (assumendo che MyEvent sia dichiarato all’interno di MyClass):

     public class MyClass { public event EventHandler MyEvent; public IEnumerable GetMyEventHandlers() { return from d in MyEvent.GetInvocationList() select (EventHandler)d; } } 

    In questo modo puoi accedere all’intero elenco di invocazioni da fuori MyClass e annullare l’iscrizione a qualsiasi gestore che desideri. Per esempio:

     myClass.MyEvent -= myClass.GetMyEventHandlers().Last(); 

    Ho scritto un post completo su questa tecnica qui .

    Tipo di approccio zoppo:

     public class SomeClass { private readonly IList _eventList = new List(); ... public event Action OnDoSomething { add { _eventList.Add(value); } remove { _eventList.Remove(value); } } } 
    1. Sovrascrivi i metodi di aggiunta / rimozione dell’evento.
    2. Mantieni un elenco di questi gestori di eventi.
    3. Se necessario, cancellali tutti e aggiungi di nuovo gli altri.

    Questo potrebbe non funzionare o essere il metodo più efficiente, ma dovrebbe portare a termine il lavoro.

    Poiché la funzione di funzioni locali C # 7.0 è stata rilasciata, l’approccio suggerito da J c diventa davvero accurato.

     void foo(object s, MyEventArgs ev) { Console.WriteLine("I did it!"); MyEvent -= foo; }; MyEvent += foo; 

    Quindi, onestamente, non hai una funzione anonima come variabile qui. Ma suppongo che la motivazione per usarla nel tuo caso possa essere applicata alle funzioni locali.

    Se si desidera essere in grado di controllare la disiscrizione, è necessario seguire il percorso indicato nella risposta accettata. Tuttavia, se sei solo preoccupato di chiarire i riferimenti quando la tua class di iscrizione esce dall’ambito, allora c’è un’altra soluzione (leggermente contorta) che implica l’utilizzo di riferimenti deboli. Ho appena pubblicato una domanda e una risposta su questo argomento.

    Una soluzione semplice:

    basta passare la variabile eventhandle come parametro a se stessa. Evento se si ha il caso che non è ansible accedere alla variabile originale creata a causa del multithreading, è ansible utilizzare questo:

     MyEventHandler foo = null; foo = (s, ev, mehi) => MyMethod(s, ev, foo); MyEvent += foo; void MyMethod(object s, MyEventArgs ev, MyEventHandler myEventHandlerInstance) { MyEvent -= myEventHandlerInstance; Console.WriteLine("I did it!"); } 

    se vuoi fare riferimento a qualche object con questo delegato, potrebbe essere ansible utilizzare Delegate.CreateDelegate (Tipo, Object target, MethodInfo methodInfo) .net considera il delegato uguale per target e methodInfo

    Se il modo migliore è quello di mantenere un riferimento sull’evento sottoscrittoHandler, questo può essere ottenuto utilizzando un dizionario.

    In questo esempio, devo utilizzare un metodo anonimo per includere il parametro mergeColumn per un set di DataGridViews.

    L’utilizzo del metodo MergeColumn con il parametro enable set su true abilita l’evento mentre lo utilizza con false disabilita tale evento.

     static Dictionary subscriptions = new Dictionary(); public static void MergeColumns(this DataGridView dg, bool enable, params ColumnGroup[] mergedColumns) { if(enable) { subscriptions[dg] = (s, e) => Dg_Paint(s, e, mergedColumns); dg.Paint += subscriptions[dg]; } else { if(subscriptions.ContainsKey(dg)) { dg.Paint -= subscriptions[dg]; subscriptions.Remove(dg); } } }