Question

Je suis donc coincé avec la réparation / maintenance d'un autre code de programmeur (blech)

Je suis un professeur convaincu de la règle "Si ce n'est pas cassé, ne le corrigez pas!" , alors que je souhaite changer quelque chose à chaque fois que je rencontre un code horrible, je me garde limité à ne changer que la quantité minimale absolue de code possible pour apporter les corrections requises. Mais dans certains cas, j'ai vraiment besoin de comprendre quelque chose avant d'essayer de le suivre / de le changer.

Je suis tombé sur ce petit morceau ici:

region.LineSelected = (x) => { };

Et je me demande si c'est pareil:

region.LineSelected = null;

Je veux être 100% positif sur ce que fait la première ligne avant de changer de méthode.

Était-ce utile?

La solution

Modifier en fonction de mon opinion actuelle sur le sujet

Ils ne sont pas les mêmes. La version lambda ajoute un gestionnaire d'événements à une méthode anonyme vide. Cela permet à d’autres codes de lever librement LineSelected () sans s’inquiéter de sa nullité (c’est-à-dire de ne pas avoir d’écoute).

Par exemple

var lineSelected = this.LineSelected;

if (lineSelected != null)
{
    lineSelected(EventArgs.Empty);
}

L'instruction ci-dessus peut générer une exception NullReferenceException si quelque chose se désabonne de LineSelected dans un autre thread après le if, mais avant l'événement. Affecter LineSelected à une variable temporaire, puis lever qui peut appeler un écouteur d'événement non abonné. L'affectation du gestionnaire d'événements à une variable locale est la méthode recommandée pour gérer les délégués nuls.

En ajoutant un délégué vide, un autre code est toujours capable d'appeler LineSelected sans craindre d'exception NullReferenceException. En affectant les délégués d'événement de multidiffusion à une variable locale, vous pouvez être sûr que la valeur ne peut pas être modifié par un autre thread.

Autres conseils

Je ne vois aucune raison d'avoir cette première ligne de code. La seule chose à laquelle je peux penser est lors de la levée de l'événement LineSelected. Si vous avez la première ligne de code de la classe, vous n'avez pas besoin de vérifier si l'événement LineSelected est nul. c'est-à-dire:

if (this.LineSelected != null)
{
   LineSelected(this,new EventArgs());
}

À la place, vous pouvez simplement déclencher l'événement sans vérification de la valeur NULL.

Cependant, avec la deuxième ligne de code, vous devrez vérifier les valeurs NULL.

Ce n'est pas un gestionnaire d'événements, c'est un simple délégué. (Un gestionnaire d'événement devra être modifié avec + = et - = pour attacher et détacher l'événement).

Comme cela a déjà été commenté, définir la propriété delegate sur un gestionnaire vide signifie qu'il n'est pas nécessaire de procéder à des vérifications nulles avant d'appeler le délégué (en supposant que rien d'autre ne puisse la définir à null).

Cela peut rendre le code d’appel plus pratique, mais il ne faut pas confondre cela avec une amélioration des performances (en éliminant la nécessité de vérifier la valeur null). Généralement, la surcharge d'un appel de méthode déléguée sera considérablement plus élevée que celle du contrôle null. Il peut y avoir des scénarios (par exemple, si le délégué a une implémentation "réelle" 99,99% du temps) dans lesquels éviter le contrôle de nullité pourrait améliorer les performances, mais il est difficile d'imaginer un scénario où une infime différence de performances pourrait avoir suffisamment d'importance pour en valoir la peine, cela ne justifierait pas également de supprimer entièrement l'invocation des délégués au profit de quelque chose de plus efficace.

Je pense que c'est une technique pour éviter les contrôles nuls sur tous les événements.

Si le code déclencheur d'événements LineSelected n'a pas de contrôle NULL correct, cela provoquera une exception:

region.LineSelected = null;

/* no event handlers added to LineSelected */

class Region {
    void OnLineSelected() {
        // Null error!
        LineSelected();
    }
}

Cependant, si un gestionnaire vide d'effet secondaire a été ajouté, le code ci-dessus fonctionnera parfaitement, même si personne n'a ajouté de gestionnaire à l'événement, car ce gestionnaire vide sera toujours attaché.

Pour développer ce que Richard et Andrew ont dit, c'est l'équivalent de

region.LineSelected = delegate {};

Ce qui signifie que lorsque vous déclenchez l'événement, vous n'avez pas besoin de vérifier d'abord null, car il a un délégué (au prix d'un petit succès)

Non, ce n'est pas la même chose - la première ligne affecte à LineSelected un délégué vide très différent de null .

Le moyen le plus simple de détecter la différence consiste à consulter le code généré par le compilateur lorsque vous utilisez la syntaxe lambda. Ce code:

using System;

class Program
{
    static void Main()
    {
        Action<int> func0 = (x) => { };
        Action<int> func1 = null;
    }
}

Compile vraiment à ceci:

internal class Program
{
    // Methods
    private static void Main()
    {
        Action<int> func0 = delegate (int x) {
        };
    }
}

Notez que le compilateur était suffisamment intelligent pour supprimer func1 car il était défini sur null et non référencé ailleurs. Mais notez que func0 reste toujours et est défini sur un délégué qui ne fait rien mais qui est très différent de null .

Ils ne sont pas identiques, car le gestionnaire d'événements est défini.

Disons que la classe qui expose LineSelected a oublié de:

if( LineSelected != null )
    LineSelected(...)

Si cette classe devait appeler LineSelected et que personne n'écoute, elle lève une exception NullReferenceException

Notez que vous pouvez également faire (dans la région) pour éviter la situation de concurrence critique:

var event = LineSelected;     if (event! = null)         événement (...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top