«La soustraction de délégués a un résultat imprévisible» dans ReSharper / C #?

124

Lors de l'utilisation de myDelegate -= eventHandlerproblèmes ReSharper (version 6):

La soustraction de délégués a un résultat imprévisible

Le rationnel derrière cela est expliqué par JetBrains ici . L'explication a du sens et, après l'avoir lue, je doute de toutes mes utilisations des -délégués.

Comment alors ,

  • puis-je écrire un événement non automatique sans rendre ReSharper grincheux?
  • ou, y a-t-il une manière meilleure et / ou «correcte» de mettre en œuvre cela?
  • ou puis-je simplement ignorer ReSharper?

Voici le code simplifié:

public delegate void MyHandler (object sender);

MyHandler _myEvent;

public event MyHandler MyEvent
{
    add
    {
        _myEvent += value;
        DoSomethingElse();
    }
    remove
    {
        _myEvent -= value; // <-- ReSharper warning here
    }
}

la source
Mono donne le même avertissement. Voici la description par R # du problème confluence.jetbrains.com/display/ReSharper/… (qui ne s'applique qu'aux listes de délégués)
thoredge

Réponses:

134

N'aie pas peur! La première partie de l'avertissement de ReSharper s'applique uniquement à la suppression de listes de délégués. Dans votre code, vous supprimez toujours un seul délégué. La deuxième partie traite de l'ordre des délégués après la suppression d'un délégué en double. Un événement ne garantit pas un ordre d'exécution pour ses abonnés, il ne vous affecte donc pas vraiment non plus.

Étant donné que les mécanismes ci-dessus peuvent conduire à des résultats imprévisibles, ReSharper émet un avertissement chaque fois qu'il rencontre un opérateur de soustraction de délégué.

ReSharper émet cet avertissement car la soustraction de délégués de multidiffusion peut avoir des pièges, il ne condamne pas entièrement cette fonctionnalité de langage. Heureusement, ces pièges sont dans des cas marginaux et il est peu probable que vous les rencontriez si vous instrumentez simplement des événements simples. Il n'y a pas de meilleure façon d'implémenter vos propres add/ removehandlers, vous devez juste en prendre note.

Je suggérerais de déclasser le niveau d'avertissement de ReSharper pour ce message sur "Hint" afin que vous ne soyez pas insensible à leurs avertissements, qui sont généralement utiles.

Allon Guralnek
la source
67
Je pense que c'est mauvais pour R # d'appeler les résultats "imprévisibles". Ils sont très clairement spécifiés. "Pas ce que l'utilisateur pourrait prédire" n'est en aucun cas la même chose que "imprévisible". (Il est également inexact de dire que le .NET Framework - définit les surcharges. Il est cuit dans le compilateur C # Delegatene pas surcharger +et -.)
Jon Skeet
6
@Jon: Je suis d'accord. Je suppose que tout le monde s'est habitué à la barre fixe que Microsoft s'est fixée. Le niveau de polonais étant aussi élevé qu'il est, avec tant de choses dans le monde .NET qui vous font «tomber dans le gouffre du succès», rencontrant une fonctionnalité de langage qui est une simple marche rapide à côté de la fosse où il y a un La chance que vous puissiez rater il est considéré par certains comme discordant et justifie un signe disant PIT OF SUCCESS IS THAT WAY --->.
Allon Guralnek
5
@AllonGuralnek: D'un autre côté, à quand remonte la dernière fois que quelqu'un a réellement eu un problème à cause de ça?
Jon Skeet
5
@Jon: Vous avez entendu parler d'un problème avec ça? Je ne connaissais même pas ce comportement avant que cette question ne soit publiée.
Allon Guralnek
2
Il est curieux que R # avertisse de la soustraction de délégués, mais pas des implémentations courantes d'événements qui ont exactement le même problème . Le problème principal est que .net utilise un seul Delegate.Combinequi "aplatit" les délégués multicast, donc si on lui donne des délégués [X, Y] et Z, il ne peut pas dire si le résultat doit être [X, Y, Z] ou [[ X, Y], Z] (ce dernier délégué tient le [X,Y]délégué comme son Target, et la Invokeméthode de ce délégué comme son Method).
supercat
28

Vous ne devez pas utiliser directement les délégués pour additionner ou soustraire. Au lieu de votre champ

MyHandler _myEvent;

Doit être également déclaré comme un événement. Cela résoudra le problème sans risquer votre solution et bénéficiera toujours de l'utilisation d'événements.

event MyHandler _myEvent;

L'utilisation de la somme ou de la soustraction du délégué est dangereuse car vous pouvez perdre des événements en affectant simplement le délégué (selon la déclaration, le développeur ne déduira pas directement qu'il s'agit d'un délégué de multidiffusion comme lorsqu'il est déclaré comme un événement). Juste pour illustrer, si la propriété mentionnée sur cette question n'a pas été signalée comme un événement, le code ci-dessous mettra en cause les deux premières affectations comme PERDUES, car quelqu'un est simplement affecté au délégué (ce qui est également valide!).

myObject.MyEvent += Method1; 
myObject.MyEvent += Method2;
myObject.MyEvent = Method3;

Lors de l'attribution de Method3, j'ai complètement perdu les deux abonnements initiaux. L'utilisation d'événements évitera ce problème et supprimera en même temps l'avertissement ReSharper.

blimac
la source
Je n'ai jamais pensé à le faire de cette façon, mais il est logique de protéger l'utilisation du délégué d'événement tant que vous gardez cet événement sous-jacent privé. Cependant, cela ne fonctionne pas bien lorsqu'il y a une synchronisation de thread plus spécifique qui doit se produire dans les gestionnaires d'ajout / suppression, tels que plusieurs abonnements à des événements ou sous-abonnements qui doivent également être suivis. Cependant, dans tous les cas, il supprime les avertissements.
Jeremy
-17

définissez-le sur = null au lieu d'utiliser - =

Jonathan Beresford
la source
5
la removeméthode d'un événement ne doit pas supprimer tous les gestionnaires, mais plutôt le gestionnaire dont la suppression a été demandée.
Servy
s'il n'en a ajouté qu'un, s'il n'en supprime qu'un seul, il les a tous supprimés. Je ne dis pas de l'utiliser dans tous les cas uniquement pour ce message de réaffectation spécifique.
Jonathan Beresford
6
Mais vous ne savez pas que le délégué supprime toujours le seul élément de la liste d'appels. Cela fait disparaître le message de réaffectation en transformant le code de travail correct en code cassé incorrect qui, dans certaines circonstances, fonctionnera par hasard, mais qui se cassera de manière inhabituelle et difficile à diagnostiquer dans de nombreux cas.
Servy
J'ai dû voter pour cela parce que -17 est une pénalité trop sévère pour quelqu'un qui prend le temps d'écrire une réponse «potentielle». +1 pour ne pas être passif, même si vous vous êtes trompé.
John C le