30 votes

Est-il possible que «ce» mot-clé soit nul?

Dans un exemple, mon professeur a implémenté Equals comme suit:

 public class Person {
    private string dni;

    // ...

    public override bool Equals(object o) {
        if (o == null)
            return (this == null);
        else {
            return ((o is Person) && (this.dni == (o as Person).dni));
        }
    }
}
 

Je n'ai aucune expérience avec C #, mais pour autant que je sache this ne peut pas être nul dans une fonction membre (au moins c'est vrai en C ++ et Java, les langages que je connais) donc le si me semble bizarre .

Ai-je raison ou y a-t-il un composant dans c # dont je ne sais pas qui rend le test this == null nécessaire?

58voto

Eric Lippert Points 300275

Je n'ai aucune expérience avec C#, mais autant que je sache, ce ne peut pas être nulle à l'intérieur d'une fonction membre (au moins c'est vrai qu'en C++ et Java, les langues que je connais)

Commençons par noter que votre affirmation est fausse.

En C++, l'envoi d'une méthode sur un nul récepteur est un comportement indéfini et un comportement indéfini signifie que tout peut arriver. "Rien du tout" inclut le programme de passage NULL comme this et continue comme si de rien n'était faux. Bien sûr, il est un peu ridicule de vérifier si this est null en C++ parce que le chèque ne peut être vrai que si vous ne connaissez pas déjà ce que votre programme est en train de faire, parce que son comportement est indéfini.

Si this peut être nul en Java je n'ai aucune idée.

Maintenant pour répondre à votre question à propos de C#. Supposons que, == n'est pas surchargé. Nous reviendrons sur ce point plus tard.

Votre méthode est écrite en C#. Supposons qu'il est appelé à partir d'un programme C# avec une valeur null récepteur. Le compilateur C# évalue si le récepteur pourrait éventuellement être nulle; si elle pourrait éventuellement être nulle alors qu'elle s'assure qu'il génère du code qui n'a nulle vérifier avant d'appeler la méthode. Par conséquent, cette vérification est inutile dans ce scénario. C'est bien sûr la 99.9999% scénario le plus probable.

Supposons qu'il est appelé par l'intermédiaire de la Réflexion, comme dans mike z de la réponse. Dans ce cas, il n'est pas le langage C# qui effectue l'invocation; plutôt, quelqu'un est délibérément abuser de la réflexion.

Supposons qu'il est appelé à partir d'une autre langue. Nous avons une méthode virtuelle; si elle est invoquée à partir de cette autre langue avec virtual envoi une valeur null vérification doit être effectuée, car sinon comment pourrions-nous savoir ce qui est dans le sous virtuel? Dans ce scénario, il ne peut pas être null.

Mais supposons qu'il est appelé à partir d'une autre langue à l'aide de non-virtuel de l'expédition. Dans ce cas, l'autre langue n'a pas besoin de mettre en œuvre le C# fonction de vérification de la valeur null. Il pourrait simplement appeler et passer la valeur null.

Il y a donc plusieurs façons d' this pourrait être null en C#, mais ils sont tous très bien sortir de l'ordinaire. Il est donc très rare pour les gens à écrire du code comme votre professeur. C#, programmeurs idiomatique supposons qu' this n'est null et de ne jamais vérifier.

Maintenant que nous avons obtenu que sur le chemin, nous allons critiquer ce code un peu plus.

public override bool Equals(object o) {
    if (o == null)
        return (this == null);
    else {
        return ((o is Person) && (this.dni == (o as Person).dni));
    }
}

Tout d'abord il y a un bug évident. Nous présumons qu' this pourrait être null, ok, nous allons l'utiliser. Ce qui l'arrête this.dni de jeter référence nulle exception??? Si vous allez à supposer qu' this peut être null alors au moins le faire de manière cohérente! (À Coverity nous nous référons à ce genre de situation comme une "avant null défaut".)

Prochaine: nous sommes primordial Equals et ensuite à l'aide d' == à l'intérieur, sans doute à la moyenne de référence de l'égalité. Cette voie est la folie! Maintenant, nous avons une situation où l' x.Equals(y) peut-être vrai, mais x==y peut être faux! C'est horrible. S'il vous plaît ne pas y aller. Si vous allez de remplacer Equals puis de surcharge == dans le même temps, et de mettre en oeuvre IEquatable<T> pendant que vous y êtes.

(Maintenant, il y a de bonnes chances que la folie réside dans les deux sens; si == est compatible avec Equals de la valeur sémantique alors personx == persony peut être différent de celui (object)personx == (object)persony, ce qui semble étrange aussi. La vente à emporter ici est que l'égalité est assez foiré en C#.)

En outre: que faire si == est remplacé plus tard? Maintenant, Equals est l'appel d'un substituée == de l'opérateur, lorsque l'auteur du code veut clairement faire une référence de comparaison. C'est une recette pour les bugs.

Mes recommandations sont (1) écrire une méthode statique qui fait la bonne chose, et (2) utiliser ReferenceEquals chaque fois que l'on pourrait peut-être être toute confusion sur ce genre d'égalité est-à-dire:

private static bool Equals(Person x, Person y)
{
    if (ReferenceEquals(x, y))
        return true;
    else if (ReferenceEquals(x, null))
        return false;
    else if (ReferenceEquals(y, null))
        return false;
    else 
        return x.dni == y.dni;
}

Que bien couvre tous les cas. Notez qu'il est clair pour le lecteur lorsque la référence à l'égalité sémantique sont destinés. Notez également que ce code fait qu'il est très facile de mettre des points d'arrêt sur chaque possibilité, à des fins de débogage. Et enfin, à noter que nous avons de prendre le moins cher possible au début; si les objets sont de référence égale alors nous n'avons pas à faire de potentiellement coûteux comparaison des champs!

Maintenant, les autres méthodes sont simples:

public static bool operator ==(Person x, Person y) 
{
  return Equals(x, y);
}
public static bool operator !=(Person x, Person y) 
{
  return !Equals(x, y);
}
public override bool Equals(object y)
{
  return Equals(this, y as Person);
}
public bool Equals(Person y)
{
  return Equals(this, y);
}

Remarquez comment beaucoup plus élégant et clair, mon chemin est que votre professeur façon. Et remarquez que ma façon de poignées null this sans jamais en comparant this de la valeur null directement.

Nouveau: tous ce qui montre que la position de compromis fut trouvé, dans lequel à la fois la valeur de référence et l'égalité sont possibles et il y a quatre (==, !=, object.Equals(object) et IEquatable<T>.Equals(T)) des moyens à mettre en œuvre l'égalité, est très compliqué et déroutant, même sans supposer que this peut ou ne peut pas être null.

Si ce sujet vous intéresse, je décris un peu plus difficile problème sur mon blog cette semaine: comment mettre en œuvre les comparaisons en général, y compris les inégalités.

http://ericlippert.com/2013/10/07/math-from-scratch-part-six-comparisons/

Les commentaires sont particulièrement intéressants comme une critique de la façon dont C# poignées de l'égalité.

Enfin: n'oubliez pas de remplacer GetHashCode. Assurez-vous que vous le faites à droite.

13voto

mike z Points 14365

Oui, dans certains cas. Le cas le plus courant est si vous appelez une méthode d'instance à l'aide d'un délégué créé par réflexion et passez un récepteur nul. Cela imprimera "True":

 public class Test
{
    public void Method()
    {
        Console.WriteLine(this == null);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        object t = new Test();
        var methodInfo = t.GetType().GetMethod("Method");
        var a = (Action)Delegate.CreateDelegate(typeof(Action), null, methodInfo);
        a();
    }
}
 

Honnêtement, même si je n'ai jamais vu quelqu'un vérifier this pour null dans le code de production.

2voto

lc. Points 50297

Une façon this pourrait == null si vous pratiquiez de la magie noire en surchargeant l'opérateur == . Par exemple, si quelqu'un décide qu'avoir une chaîne nulle ou vide dni équivaut à une personne nulle:

 public static bool operator ==(Person a, Person b)
{
    if(String.IsNullOrEmpty(a.dni) && (object)b == null)
        return true;

    if(String.IsNullOrEmpty(b.dni) && (object)a == null)
        return true;

    return Object.ReferenceEquals(a, b);
}
 

Notez que c'est probablement une très mauvaise idée.

2voto

Hans Passant Points 475940

Oui, il est possible et, en fait, pas totalement improbable. Le langage C# donne une excellente garantie qu'aucune méthode ne peut être appelée sur un nul référence à un objet de classe, il génère un code qui permet d'null vérifier sur le site d'appel. Ce qui n'est certainement éviter beaucoup de casse-tête, une exception NullReferenceException à l'intérieur de la méthode peut être assez difficile à déboguer sans cette vérification.

Cette vérification est cependant spécifique à C#, pas tous .NET languages mettre en œuvre. Le C++/CLI la langue n'est pas par exemple. Vous pouvez voir par vous-même par la création d'une solution avec un C++ Console CLR mode projet et une bibliothèque de classes C#. Ajouter une référence dans le CLR de projet pour le projet C#.

Le code C#:

using System;

namespace ClassLibrary1 {
    public class Class1 {
        public void NullTest() {
            if (this == null) Console.WriteLine("It's null");
        }
    }
}

C++/CLI code:

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args) {
    ClassLibrary1::Class1^ obj = nullptr;
    obj->NullTest();
    Console::ReadLine();
    return 0;
}

Sortie:

C'est null

Ce n'est pas un comportement indéfini ou illégal. Il n'est pas verboten par la CLI spec. Et tant que la méthode n'a pas accès à tout les membres de l'instance alors que rien ne va mal. Le CLR gère ainsi, une NullReferenceException est également généré pour les pointeurs qui ne sont pas null. Comme il sera quand NullTest() accède à un champ qui n'est pas le premier champ dans la classe.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X