web-dev-qa-db-fra.com

Remplacement == opérateur. Comment comparer à null?

Duplicate possible:
Comment vérifier les valeurs NULL dans une surcharge d’opérateur ‘==’ sans récursion infinie?

Il y a probablement une réponse facile à cela ... mais cela semble m'échapper. Voici un exemple simplifié:

public class Person
{
   public string SocialSecurityNumber;
   public string FirstName;
   public string LastName;
}

Supposons que, pour cette application particulière, il soit valide de dire que si les numéros de sécurité sociale correspondent et que les deux noms correspondent, nous faisons alors référence à la même "personne".

public override bool Equals(object Obj)
{
    Person other = (Person)Obj;
    return (this.SocialSecurityNumber == other.SocialSecurityNumber &&
        this.FirstName == other.FirstName &&
        this.LastName == other.LastName);
}

Pour que les choses restent cohérentes, nous substituons également les opérateurs == et! = Aux développeurs de l’équipe qui n’utilisent pas le .Equals méthode.

public static bool operator !=(Person person1, Person person2)
{
    return ! person1.Equals(person2);
}

public static bool operator ==(Person person1, Person person2)
{
    return person1.Equals(person2);
}

Bien et gentil, non?

Cependant, que se passe-t-il lorsqu'un objet Person est null?

Vous ne pouvez pas écrire:

if (person == null)
{
    //fail!
}

Dans la mesure où cela entraînera l'exécution du remplacement par l'opérateur ==, le code échouera dans les cas suivants:

person.Equals()

appel de méthode, car vous ne pouvez pas appeler une méthode sur une instance nulle.

D'autre part, vous ne pouvez pas vérifier explicitement cette condition dans le remplacement de ==, car cela provoquerait une récursion infinie (et un débordement de pile [dot com]).

public static bool operator ==(Person person1, Person person2)
{
    if (person1 == null)
    {
         //any code here never gets executed!  We first die a slow painful death.
    }
    return person1.Equals(person2);
}

Alors, comment substituez-vous les opérateurs == et! = Pour l'égalité des valeurs tout en tenant compte des objets nuls?

J'espère que la réponse n'est pas terriblement simple. :-)

125
Flipster

Utilisez object.ReferenceEquals(person1, null) au lieu du == opérateur:

public static bool operator ==(Person person1, Person person2)
{
    if (object.ReferenceEquals(person1, null))
    {
         return object.ReferenceEquals(person2, null);
    }

    return person1.Equals(person2);
}
234
cdhowie

Je l'ai toujours fait de cette façon (pour les opérateurs == et! =) Et je réutilise ce code pour chaque objet que je crée:

public static bool operator ==(Person lhs, Person rhs)
{
    // If left hand side is null...
    if (System.Object.ReferenceEquals(lhs, null))
    {
        // ...and right hand side is null...
        if (System.Object.ReferenceEquals(rhs, null))
        {
            //...both are null and are Equal.
            return true;
        }

        // ...right hand side is not null, therefore not Equal.
        return false;
    }

    // Return true if the fields match:
    return lhs.Equals(rhs);
}

"! =" va alors comme ceci:

public static bool operator !=(Person lhs, Person rhs)
{
    return !(lhs == rhs);
}

Modifier
J'ai modifié le == fonction opérateur correspondant à la mise en oeuvre suggérée par Microsoft ici .

17
Mike Webb

vous pouvez toujours passer outre et mettre

(Object)(person1)==null

J'imagine que cela fonctionnerait, pas sûr cependant.

11
rtpg

La routine finale (hypothétique) est ci-dessous. Cela ressemble beaucoup à la première réponse acceptée de @ cdhowie.

public static bool operator ==(Person person1, Person person2)
{
    if (Person.ReferenceEquals(person1, person2)) return true;
    if (Person.ReferenceEquals(person1, null)) return false; //*
    return person1.Equals(person2);
}

Merci pour les bonnes réponses!

// * - .Equals() effectue le contrôle nul sur personne2

3
Flipster

Convertissez l'instance Person en object:

public static bool operator ==(Person person1, Person person2)
{
    if ((object)person1 == (object)person2) return true;
    if ((object)person1 == null) return false;
    if ((object)person2 == null) return false;
    return person1.Equals(person2);
}
2
dtb

Plus facile que n'importe laquelle de ces approches serait de simplement utiliser

public static bool operator ==(Person person1, Person person2)   
{   
    EqualityComparer<Person>.Default.Equals(person1, person2)
} 

Cela a la même sémantique d'égalité nulle que les approches proposées par tous les autres, mais c'est le problème du cadre pour en comprendre les détails :)

1
start

Surcharger constamment ces opérateurs est assez difficile. Ma réponse à une question connexe peut servir de modèle.

Fondamentalement, vous devez d’abord faire une référence (object.ReferenceEquals) teste si l'objet est null. Puis vous appelez Equals.

1
Konrad Rudolph

cdhowie est sur l’argent avec l’utilisation de ReferenceEquals, mais il est à noter que vous pouvez toujours obtenir une exception si quelqu'un passe null directement à Equals. En outre, si vous voulez remplacer Equals, il vaut presque toujours la peine d'implémenter IEquatable<T>, C'est pourquoi je l'aurais plutôt.

public class Person : IEquatable<Person>
{
  /* more stuff elided */

  public bool Equals(Person other)
  {
    return !ReferenceEquals(other, null) &&
      SocialSecurityNumber == other.SocialSecurityNumber &&
      FirstName == other.FirstName &&
      LastName == other.LastName;
  }
  public override bool Equals(object obj)
  {
    return Equals(obj as Person);
  }
  public static bool operator !=(Person person1, Person person2)
  {
    return !(person1 == person2);
  }
  public static bool operator ==(Person person1, Person person2)
  {
    return ReferenceEquals(person1, person2)
      || (!ReferenceEquals(person1, null) && person1.Equals(person2));
  }
}

Et bien sûr, vous ne devriez jamais remplacer Equals ni GetHashCode()

public override int GetHashCode()
{
   //I'm going to assume that different
   //people with the same SocialSecurityNumber are extremely rare,
   //as optimise by hashing on that alone. If this isn't the case, change this
   return SocialSecurityNumber.GetHashCode();
}

Il convient également de noter que l’identité implique l’égalité (c’est-à-dire que pour tout concept valable d’égalité, quelque chose est toujours égal à lui-même). Étant donné que les tests d'égalité peuvent être coûteux et se dérouler en boucle, et que la comparaison de quelque chose avec lui-même a tendance à être assez courante dans le code réel (en particulier si des objets sont passés à plusieurs endroits), il peut être intéressant d'ajouter un raccourci:

  public bool Equals(Person other)
  {
    return !ReferenceEquals(other, null) &&
      ReferenceEquals(this, other) ||
      (
        SocialSecurityNumber == other.SocialSecurityNumber &&
        FirstName == other.FirstName &&
        LastName == other.LastName
      );
  }

La réduction des avantages sur ReferenceEquals(this, other) peut varier considérablement en fonction de la nature de la classe. Toutefois, il convient de toujours prendre en compte la question de savoir si cela vaut la peine d'être fait ou non. ici.

1
Jon Hanna

Transformez la personne en objet puis effectuez la comparaison:

object o1 = (object)person1;
object o2 = (object)person2;
if(o1==o2) //compare instances.
   return true;
if (o1 == null || o2 == null)  //compare to null.
   return false;
//continue with Person logic.
1
Greg Sansom