Méthode de désabonnement anonyme en C#

est-il possible de désabonner une méthode anonyme d'un événement?

si je m'abonne à un événement comme celui-ci:

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

MyEvent += MyMethod;

je peux désabonner comme ceci:

MyEvent -= MyMethod;

mais si je m'abonne en utilisant une méthode anonyme:

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

est-il possible de désabonner cette méthode anonyme? Si oui, comment?

199
demandé sur Eric 2008-10-08 19:24:46

11 réponses

Action myDelegate = delegate(){Console.WriteLine("I did it!");};

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

faites simplement référence au délégué.

203
répondu Jacob Krall 2012-04-13 19:50:55

Une technique consiste à déclarer une variable pour contenir la méthode anonyme qui sera disponible à l'intérieur de la méthode anonyme. Cela a fonctionné pour moi parce que le comportement désiré était de se désabonner après que l'événement a été manipulé.

exemple:

MyEventHandler foo = null;
foo = delegate(object s, MyEventArgs ev)
    {
        Console.WriteLine("I did it!");
        MyEvent -= foo;
    };
MyEvent += foo;
137
répondu J c 2008-10-08 15:34:51

de mémoire, la spécification ne garantit pas explicitement le comportement de toute façon quand il s'agit de l'équivalence des délégués créés avec des méthodes anonymes.

si vous avez besoin de vous désabonner, vous devriez soit utiliser une méthode" normale " ou garder le délégué ailleurs pour vous désabonner avec exactement le même délégué que vous aviez l'habitude de vous inscrire.

18
répondu Jon Skeet 2008-10-08 15:26:56

en 3.0 peut être raccourci à:

MyHandler myDelegate = ()=>Console.WriteLine("I did it!");
MyEvent += myDelegate;
...
MyEvent -= myDelegate;
17
répondu 2009-07-23 01:53:51

au lieu de garder une référence à n'importe quel délégué vous pouvez Instrumenter votre classe afin de donner la liste d'invocation de l'événement de nouveau à l'appelant. Fondamentalement, vous pouvez écrire quelque chose comme ceci (en supposant que mon événement est déclaré à L'intérieur de MyClass):

public class MyClass 
{
  public event EventHandler MyEvent;

  public IEnumerable<EventHandler> GetMyEventHandlers()  
  {  
      return from d in MyEvent.GetInvocationList()  
             select (EventHandler)d;  
  }  
}

pour que vous puissiez accéder à la liste complète des invocations depuis L'extérieur de MyClass et vous désabonner de tout gestionnaire que vous voulez. Par exemple:

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

j'ai écrit un post complet sur cette technique ici .

9
répondu hemme 2011-06-23 23:11:50

minable approche:

public class SomeClass
{
  private readonly IList<Action> _eventList = new List<Action>();

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. outrepasser l'événement Ajouter/Supprimer les méthodes.
  2. Tenir une liste de ces gestionnaires d'événements.
  3. au besoin, éliminez-les tous et ajoutez les autres.

Cela peut ne pas fonctionner ou être la méthode la plus efficace, mais devrait faire le travail.

6
répondu casademora 2008-10-08 15:37:24

depuis la sortie de la fonctionnalité C# 7.0 fonctions locales , l'approche suggérée par J C devient vraiment soignée.

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

donc, honnêtement, vous n'avez pas de fonction anonyme comme variable ici. Mais je suppose que la motivation à utiliser dans votre cas peut être appliquée à des fonctions locales.

3
répondu mazharenko 2017-08-15 05:30:06

Si vous voulez être en mesure de contrôler le désabonnement vous devez alors suivre l'itinéraire indiqué dans votre réponse. Cependant, si vous êtes juste soucieux de clarifier les références lorsque votre classe d'abonnement sort de la portée, alors il y a une autre solution (légèrement alambiquée) qui implique l'utilisation de références faibles. Je viens de poster une question et réponse sur ce sujet.

2
répondu Benjol 2017-05-23 12:34:18

Une solution simple:

passe simplement la variable eventhandle comme paramètre à lui-même. Event si vous avez le cas où vous ne pouvez pas accéder à la variable d'origine créée à cause du multithreading, vous pouvez utiliser ceci:

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!");
}
1
répondu Manuel Marhold 2014-07-12 14:11:21

si vous voulez faire référence à un objet avec ce délégué, peut-être vous pouvez utiliser Délégué.CreateDelegate (Type, cible de L'objet, methodInfo methodInfo)) .net considérer le délégué est égal par cible et methodInfo

0
répondu user3217549 2014-01-21 03:19:30

si la meilleure façon est de garder une référence sur le eventHandler souscrit, cela peut être réalisé en utilisant un dictionnaire.

dans cet exemple, je dois utiliser une méthode anonyme pour inclure le paramètre mergeColumn pour un ensemble de DataGridViews.

utilisant la méthode MergeColumn avec le paramètre enable défini à true active l'événement tout en l'utilisant avec false Le désactive.

static Dictionary<DataGridView, PaintEventHandler> subscriptions = new Dictionary<DataGridView, PaintEventHandler>();

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);
        }
    }
}
0
répondu Larry 2016-10-13 06:40:33