J'ai souvent entendre/lire les conseils suivants:
Toujours faire une copie d'un événement avant de le vérifier pour null
et le feu. Cela permettra d'éliminer un problème potentiel avec filetage, où l'événement est d' null
à l'emplacement de droite entre le point où vous vérifier la valeur null et où vous le feu de l'événement:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Mise à jour: j'ai pensé à partir de la lecture sur les optimisations que cela pourrait également exiger de l'événement membre à être volatile, mais Jon Skeet unis dans sa réponse que le CLR n'optimisent pas loin de la copie.
Mais en attendant, pour que cette question de même, un autre thread doit avoir fait quelque chose comme ceci:
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
La séquence réelle pourrait être ce mélange:
// Copy the event delegate before checking/calling
EventHandler copy = TheEvent;
// Better delist from event - don't want our handler called from now on:
otherObject.TheEvent -= OnTheEvent;
// Good, now we can be certain that OnTheEvent will not run...
if (copy != null)
copy(this, EventArgs.Empty); // Call any handlers on the copied list
Le problème étant que l' OnTheEvent
s'exécute après que l'auteur a désabonné, et pourtant ils ont juste désabonné en particulier pour éviter que cela se produise. Sûrement, ce qui est vraiment nécessaire est un événement personnalisé de mise en œuvre appropriée de la synchronisation dans l' add
et remove
accesseurs. Et en plus il y a le problème des blocages si un verrou est détenu alors qu'un événement est déclenché.
Est-ce donc le Culte du Cargo de la Programmation? Il semble de cette façon, beaucoup de gens doivent être de prendre cette mesure pour protéger leur code à partir de plusieurs threads, alors qu'en réalité, il me semble que les événements exigent beaucoup plus de soins que ce, avant qu'ils puissent être utilisés dans le cadre d'un multi-thread de conception. Par conséquent, les gens qui ne prennent pas que des soins supplémentaires pourrait tout aussi bien ignorer ce conseil - il n'est tout simplement pas un problème pour single-threaded programmes, et en fait, compte tenu de l'absence d' volatile
dans la plupart des en ligne un exemple de code, le conseil a peut-être eu aucun effet du tout.
(Et n'est-il pas beaucoup plus simple pour attribuer le vide delegate { }
sur la déclaration d'un membre de sorte que vous n'avez jamais besoin de vérifier pour null
en premier lieu?)
Mise à jour: Dans le cas où c'était pas clair, je l'ai fait saisir l'intention du conseil - d'éviter une référence nulle exception, dans toutes les circonstances. Mon point c'est que cette exception référence nulle ne peut se produire si un autre thread est la radiation de l'événement, et la seule raison pour cela est de s'assurer que pas d'autres appels seront reçus par l'intermédiaire de l'événement, qui clairement n'est PAS obtenue par cette technique. Vous seriez cacher une condition de concurrence serait - il préférable de le révéler! Que nulle exception permet de détecter un abus de votre composant. Si vous souhaitez que votre composant à être protégés contre les abus, vous pouvez suivre l'exemple de WPF - stocker l'ID de thread dans votre constructeur et ensuite lancer une exception si un autre thread tente d'interagir directement avec votre composant. Ou encore de mettre en œuvre une véritable thread-safe composant (pas une tâche facile).
Donc je pense que le simple fait de faire ce copier/check idiome de culte du cargo de la programmation, de l'ajout de désordre et le bruit de votre code. Pour réellement protéger contre d'autres threads nécessite beaucoup plus de travail.
Mise à jour en réponse à Eric Lippert du blog:
Donc, il y a une chose importante que j'avais raté sur les gestionnaires d'événements: "les gestionnaires d'événements sont nécessaires pour être robuste en face de l'être, même après que l'événement a été désabonné", et, évidemment, donc nous avons seulement besoin de se soucier de la possibilité de l'événement délégué null
. Cette exigence sur les gestionnaires d'événements documentés n'importe où?
Et donc: "Il y a d'autres façons de résoudre ce problème; par exemple, l'initialisation du gestionnaire d'avoir un vide d'action qui n'est jamais supprimé. Mais ce faisant, null vérifier est que le modèle standard."
Donc le restant un fragment de ma question est, pourquoi est explicite-null-cocher la case "modèle standard"? L'alternative, d'attribuer le vide délégué, ne nécessite qu' = delegate {}
à être ajouté à l'événement de la déclaration, et cela élimine ces petits tas de stinky cérémonie de chaque lieu où l'événement est déclenché. Il serait facile de assurez-vous que le vide d'un délégué n'est pas cher pour instancier. Ou suis-je manque encore quelque chose?
Certes, il faut que (comme Jon Skeet suggéré) ce qui est juste .NET 1.x conseil qui n'a pas disparu, comme il aurait dû le faire en 2005?