web-dev-qa-db-fra.com

Se désabonner de la méthode 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 me 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 se désabonner de cette méthode anonyme? Si c'est le cas, comment?

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

MyEvent += myDelegate;


// .... later

MyEvent -= myDelegate;

Il suffit de garder une référence au délégué.

208
Jacob Krall

Une technique consiste à déclarer qu'une variable contient la méthode anonyme qui serait alors disponible dans la méthode anonyme elle-même. Cela a fonctionné pour moi car le comportement souhaité était de se désabonner après la gestion de l'événement.

Exemple:

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

En mémoire, la spécification ne garantit pas explicitement le comportement des deux côtés en ce qui concerne l’équivalence des délégués créés avec des méthodes anonymes.

Si vous devez vous désabonner, utilisez une méthode "normale" ou conservez le délégué ailleurs afin de pouvoir vous désabonner avec exactement le même délégué que vous aviez l'habitude de vous abonner.

21
Jon Skeet

Dans 3.0 peut être réduit à:

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

Au lieu de garder une référence à un délégué, vous pouvez instrumenter votre classe afin de rendre la liste d'invocation de l'événement à l'appelant. En gros, vous pouvez écrire quelque chose comme ceci (en supposant que MyEvent soit déclaré dans MyClass):

public class MyClass 
{
  public event EventHandler MyEvent;

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

Ainsi, vous pouvez accéder à toute la liste d’invocations depuis l’extérieur de MyClass et désinscrire tout gestionnaire souhaité. Par exemple:

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

J'ai écrit un article complet sur cette technique ici .

9
hemme

Genre d'approche boiteuse:

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

  ...

  public event Action OnDoSomething
  {
    add {
      _eventList.Add(value);
    }
    remove {
      _eventList.Remove(value);
    }
  }
}
  1. Remplacez les méthodes d'ajout/suppression d'événement.
  2. Gardez une liste de ces gestionnaires d'événements.
  3. Au besoin, effacez-les tous et rajoutez les autres.

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

6
casademora

Depuis que les fonctions locales de C # 7.0 feature ont été publiées, 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 en tant que variable ici. Mais je suppose que la motivation de l’utiliser dans votre cas peut s’appliquer aux fonctions locales.

4
mazharenko

Si vous voulez pouvoir contrôler la désinscription, vous devez suivre l'itinéraire indiqué dans votre réponse acceptée. Cependant, si vous souhaitez uniquement clarifier les références lorsque votre classe d'abonnement sort de son périmètre, il existe une autre solution (légèrement compliquée) qui implique l'utilisation de références faibles. Je viens de poster une question et réponse sur ce sujet.

2
Benjol

Une solution simple:

il suffit de passer la variable eventhandle en tant que paramètre pour elle-même . Event si vous avez le cas où vous ne pouvez pas accéder à la variable créée à l'origine en raison de la lecture multiple, 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
Manuel Marhold

Si le meilleur moyen consiste à conserver une référence sur le gestionnaire d'événements abonné, vous pouvez utiliser un dictionnaire.

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

L'utilisation de la méthode MergeColumn avec le paramètre enable défini sur true active l'événement, mais son utilisation 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
Larry

si vous voulez faire référence à un objet avec ce délégué, vous pouvez éventuellement utiliser Delegate.CreateDelegate (Type, Object target, MethodInfo methodInfo) . net considérer le délégué est égal à target et methodInfo

0
user3217549