Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les opérateurs == (égalité) et != (inégalité) vérifient si leurs opérandes sont égaux ou non. Les types de valeurs sont égaux lorsque leur contenu est égal. Les types de référence sont égaux lorsque les deux variables font référence au même stockage.
La documentation de référence du langage C# décrit la version la plus récente du langage C#. Il contient également la documentation initiale des fonctionnalités dans les préversions publiques pour la prochaine version du langage.
La documentation identifie toute fonctionnalité introduite en premier dans les trois dernières versions de la langue ou dans les préversions publiques actuelles.
Conseil / Astuce
Pour savoir quand une fonctionnalité a été introduite en C#, consultez l’article sur l’historique des versions du langage C#.
Vous pouvez utiliser l’opérateur is de correspondance de modèle comme alternative à un == test lorsque vous testez sur une valeur constante. L’opérateur is utilise la sémantique d’égalité par défaut pour tous les types valeur et référence.
Opérateur d’égalité ==
L’opérateur d’égalité == retourne true si ses opérandes sont égaux, false dans le cas contraire.
Égalité de types valeur
Les opérandes des types valeur intégrés sont égaux si leurs valeurs sont égales :
int a = 1 + 2 + 3;
int b = 6;
Console.WriteLine(a == b); // output: True
char c1 = 'a';
char c2 = 'A';
Console.WriteLine(c1 == c2); // output: False
Console.WriteLine(c1 == char.ToLower(c2)); // output: True
Notes
Pour les ==opérateurs , ,<><=>= et si l’un des opérandes n’est pas un nombre (Double.NaN ou Single.NaN), le résultat de l’opération est .false Cette condition signifie que la NaN valeur n’est pas supérieure, inférieure ou égale à une autre double valeur (ou float) y compris NaN. Pour plus d’informations et des exemples, consultez l’article de référence Double.NaN ou Single.NaN.
Deux opérandes du même type enum sont égaux si les valeurs correspondantes du type intégral sous-jacent sont égales.
Par défaut, les types struct définis par l’utilisateur ne prennent pas en charge l’opérateur ==. Pour prendre en charge l’opérateur ==, un struct défini par l’utilisateur doit le surcharger.
Les tuples C# prennent en charge les opérateurs et == les != opérateurs intégrés. Pour plus d’informations, consultez la section Égalité des tuples de l’article Types de tuples.
Égalité de types référence
Par défaut, les opérandes de type référence, à l’exception des enregistrements, sont égaux s’ils font référence au même objet :
public class ReferenceTypesEquality
{
public class MyClass
{
private int id;
public MyClass(int id) => this.id = id;
}
public static void Main()
{
var a = new MyClass(1);
var b = new MyClass(1);
var c = a;
Console.WriteLine(a == b); // output: False
Console.WriteLine(a == c); // output: True
}
}
Comme l’illustre l’exemple précédent, les types de référence définis par l’utilisateur prennent en charge l’opérateur == par défaut. Un type référence peut toutefois surcharger l’opérateur ==. Si un type référence surcharge l’opérateur ==, utilisez la méthode Object.ReferenceEquals pour vérifier si deux références de ce type font référence au même objet.
Égalité des types d’enregistrements
Les types d’enregistrements prennent en charge les opérateurs == et != qui fournissent par défaut une sémantique d’égalité des valeurs. Autrement dit, deux opérandes d’enregistrement sont égaux lorsque les deux sont null ou des valeurs correspondantes de tous les champs et que les propriétés implémentées automatiquement sont égales.
public class RecordTypesEquality
{
public record Point(int X, int Y, string Name);
public record TaggedNumber(int Number, List<string> Tags);
public static void Main()
{
var p1 = new Point(2, 3, "A");
var p2 = new Point(1, 3, "B");
var p3 = new Point(2, 3, "A");
Console.WriteLine(p1 == p2); // output: False
Console.WriteLine(p1 == p3); // output: True
var n1 = new TaggedNumber(2, new List<string>() { "A" });
var n2 = new TaggedNumber(2, new List<string>() { "A" });
Console.WriteLine(n1 == n2); // output: False
}
}
Comme l’illustre l’exemple précédent, l’égalité des membres de type référence est comparée à l’aide de leurs implémentations d’égalité spécifiques.
Égalité de chaîne
Deux opérandes string sont égaux lorsque les deux sont null ou lorsque les deux instances de chaîne sont de même longueur et ont des caractères identiques dans chaque position de caractère :
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
Les comparaisons d’égalité de chaîne sont des comparaisons ordinales respectant la casse. Pour plus d’informations sur la comparaison de chaînes, consultez Comment comparer des chaînes en C#.
Égalité de délégué
Deux opérandes délégués du même type d’exécution sont égaux lorsque les deux sont null ou leurs listes d’appel sont de la même longueur et ont des entrées égales dans chaque position :
Action a = () => Console.WriteLine("a");
Action b = a + a;
Action c = a + a;
Console.WriteLine(object.ReferenceEquals(b, c)); // output: False
Console.WriteLine(b == c); // output: True
Important
Les entrées égales dans une liste d’appels incluent tous les paramètres fixes dans l’appel, y compris le récepteur. Le récepteur est l’instance d’un objet représenté par this l’appel de l’entrée.
var o1 = new object();
var o2 = new object();
var d1 = o1.ToString;
var d2 = o2.ToString;
Console.WriteLine(object.ReferenceEquals(d1, d2)); // output: False
Console.WriteLine(d1 == d2); // output: False (different receivers)
Pour plus d’informations, consultez la section Opérateurs d’égalité de délégué de la spécification du langage C#.
Les délégués provenant de l’évaluation d’expressions lambda identiques sémantiquement ne sont pas égaux, comme l’illustre l’exemple suivant :
Action a = () => Console.WriteLine("a");
Action b = () => Console.WriteLine("a");
Console.WriteLine(a == b); // output: False
Console.WriteLine(a + b == a + b); // output: True
Console.WriteLine(b + a == a + b); // output: False
Opérateur d’inégalité !=
L’opérateur != d’inégalité retourne true si ses opérandes ne sont pas égaux et false sinon. Pour les opérandes des types intégrés, l’expression x != y produit le même résultat que l’expression !(x == y). Pour plus d’informations sur l’égalité des types, consultez la section Opérateur d’égalité.
L’exemple suivant montre comment utiliser l’opérateur != :
int a = 1 + 1 + 2 + 3;
int b = 6;
Console.WriteLine(a != b); // output: True
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 != s2); // output: False
object o1 = 1;
object o2 = 1;
Console.WriteLine(o1 != o2); // output: True
Égalité dans les hiérarchies de classes
Les enregistrements gèrent correctement l’héritage sans travail manuel. L’égalité générée par le compilateur vérifie à la fois le type d’exécution et toutes les propriétés déclarées, de sorte qu’elle répond automatiquement aux exigences de symétrie et de transitivité. Préférez record une hiérarchie non scellée manuelle lorsque l’égalité des valeurs est l’objectif.
Important
Utilisez record chaque fois que cela est possible : le compilateur génère tous les membres d’égalité requis pour vous. L’implémentation manuelle n’est nécessaire que lorsque votre type doit dériver d’une classe non-enregistrement ou a d’autres contraintes qui empêchent record.
Implémenter l’égalité manuellement lorsqu’un type ne peut pas être un record
Voici une implémentation manuelle minimale pour un type valeur qui ne peut pas être un enregistrement :
class Color : IEquatable<Color>
{
public Color(int r, int g, int b)
{
R = r;
G = g;
B = b;
}
public int R { get; }
public int G { get; }
public int B { get; }
public bool Equals(Color? other) =>
other is not null && R == other.R && G == other.G && B == other.B;
public override bool Equals(object? obj) => obj is Color other && Equals(other);
public override int GetHashCode() => HashCode.Combine(R, G, B);
}
L’implémentation fournit trois membres requis : Equals(T?) en tant que comparaison principale, override Equals(object?) pour les appels au niveau de l’objet, et override GetHashCode() ainsi les collections basées sur le hachage fonctionnent correctement.
HashCode.Combine est un helper de bibliothèque qui génère un hachage à partir des mêmes valeurs utilisées par Equals. L’implémentation IEquatable<T> (la Equals(T?) surcharge) est facultative, mais évite la boxe lorsque les appelants ont déjà le type concret.
Lorsque vous définissez == également et !=que la langue les requiert en tant que paire ; les avertissements CS0660 et CS0661 vous rappellent de garder les quatre membres cohérents.
Avec les trois membres ci-dessus en place, Equals reflète l’égalité des valeurs, mais == teste toujours l’identité, car aucun opérateur n’a == encore été déclaré :
var red1 = new Color(255, 0, 0);
var red2 = new Color(255, 0, 0);
Console.WriteLine(red1.Equals(red2)); // => True
Console.WriteLine(red1 == red2); // => False (no == overload; identity check)
Une implémentation correcte doit également satisfaire le contrat d’équivalence (supposez x, yet z ne sont pas null) :
-
Réflexive :
x.Equals(x)retournetrue. -
Symétrique :
x.Equals(y)retourne la même valeur quey.Equals(x). -
Transitif : si
x.Equals(y)ety.Equals(z)sont les deuxtrue, ilx.Equals(z)doit êtretrue. -
Cohérent : appels successifs pour
x.Equals(y)retourner la même valeur tant que aucun objet ne change. -
Comportement Null : renvoie
false;x.Equals(y)x.Equals(null)ne doit pas lever lorsqu’il est appelé sur une valeur non nullx.
L’égalité des valeurs dans une hiérarchie de classes non scellée nécessite plus de soin que dans une classe scellée pour satisfaire les règles symétriques et transitives. Le risque est que la IEquatable<T>.Equals(T? other) répartition suit le type déclaré (le type écrit dans la déclaration de variable) de la variable, et non son type d’exécution. Si Shape elle déclare un non-virtualEquals(Shape? other), une variable typée comme Shape qui contient un Circle appel Shape.Equalsau moment de l’exécution , ignorant Circlesilencieusement les champs spécifiques à -spécifiques. Deux Circle objets avec des rayons différents peuvent être comparés comme égaux lorsqu’ils sont accessibles via une Shape variable.
Le modèle correct nécessite deux exigences de coopération : rendre la méthode typée Equals afin que chaque classe dérivée puisse étendre la comparaison et ajouter un GetType() == other.GetType() garde dans l’implémentation de classe de base afin que les objets de différents types d’exécution ne soient jamais considérés comme virtual égaux.
Implémentation de classe de base
// Shape is an unsealed base class. Making Equals virtual and guarding with GetType()
// ensures a derived instance is never equal to an instance of a different runtime type.
class Shape : IEquatable<Shape>
{
public string Color { get; }
public Shape(string color) => Color = color;
public override bool Equals(object? obj) => Equals(obj as Shape);
// virtual so derived classes can override and augment the comparison
public virtual bool Equals(Shape? other) =>
other is not null &&
GetType() == other.GetType() && // reject different runtime types
Color == other.Color;
// GetType() is included because equality requires matching runtime types
public override int GetHashCode() => HashCode.Combine(GetType(), Color);
public static bool operator ==(Shape? l, Shape? r) => l?.Equals(r) ?? r is null;
public static bool operator !=(Shape? l, Shape? r) => !(l == r);
}
Points clés :
-
virtualtypéEquals: chaque classe dérivée remplace cette méthode pour augmenter la comparaison avec ses propres champs. -
GetType()guard :GetType() == other.GetType()empêche l’égalité d’unCircleShapeavec la même couleur et empêche les objets de différents types dérivés de s’égaler les uns les autres. -
GetHashCodeinclutGetType(): car deux objets sont égaux uniquement lorsque leurs types d’exécution correspondent,GetHashCodedoivent hacher le type d’exécution ainsi que les champs de données. OmettreGetType()ici provoque un comportement incorrect dansDictionary<TKey,TValue>etHashSet<T>. -
==délégués àEquals: maintient l’égalité des opérateurs et des méthodes cohérentes.
Implémentation de classe dérivée
Une classe dérivée qui ajoute des champs remplace le type, Equalseffectue un cast en son propre type, appels base.Equals, puis compare ses propres champs :
class Circle : Shape
{
public double Radius { get; }
public Circle(string color, double radius) : base(color) => Radius = radius;
public override bool Equals(object? obj) => Equals(obj as Shape);
// Calls base.Equals to verify Color and runtime type, then adds Radius
public override bool Equals(Shape? other) =>
other is Circle c && base.Equals(c) && Radius == c.Radius;
public override int GetHashCode() => HashCode.Combine(GetType(), Color, Radius);
}
base.Equals(c) applique la GetType() protection et vérifie les champs partagés. Le cast via other is Circle c échoue rapidement lorsque l’argument est un Shape autre type dérivé.
Utilisation par le biais d’une variable de type base
Shape circle1 = new Circle("red", 5.0);
Shape circle2 = new Circle("red", 7.0);
Shape circle3 = new Circle("red", 5.0);
Shape shape1 = new Shape("red");
Console.WriteLine(circle1.Equals(circle2)); // => False (Radius differs)
Console.WriteLine(circle1.Equals(circle3)); // => True
Console.WriteLine(circle1.Equals(shape1)); // => False (different runtime types)
Les classes scellées sont plus simples
Vous ne pouvez pas sous-classer une sealed classe. Par conséquent, les types de compilation et d’exécution sont toujours d’accord. Vous n’avez pas besoin de la garde ou virtual de la GetType() répartition. Le IEquatable<T> modèle indiqué dans Implémenter l’égalité vous-même lorsqu’un type ne peut pas être un enregistrement est correct et complet pour une classe scellée.
Capacité de surcharge de l’opérateur
Vous pouvez surcharger les opérateurs et == les != opérateurs dans un type défini par l’utilisateur. Si vous surchargez l’un de ces deux opérateurs, vous devez également surcharger l’autre opérateur.
Vous ne pouvez pas surcharger explicitement les opérateurs et == les != opérateurs dans un type d’enregistrement. Pour modifier le comportement des opérateurs et == du != type Td’enregistrement, implémentez la IEquatable<T>.Equals méthode avec la signature suivante :
public virtual bool Equals(T? other);
spécification du langage C#
Pour plus d’informations, consultez la section Opérateurs relationnels et de test de type de la spécification du langage C#.
Pour plus d’informations sur l’égalité des types d’enregistrements, consultez la section Membres d’égalité de la spécification du langage C#.