Partager via


CA2224 : Remplacez Equals au moment de surcharger l'opérateur égal

TypeName

OverrideEqualsOnOverloadingOperatorEquals

CheckId

CA2224

Catégorie

Microsoft.Usage

Modification avec rupture

Modification sans rupture

Cause

Un type public implémente l'opérateur d'égalité, mais ne se substitue pas à Object.Equals.

Description de la règle

L'opérateur d'égalité est destiné à fournir un accès syntaxique pratique aux fonctionnalités de la méthode Equals. Si vous implémentez l'opérateur d'égalité, sa logique doit être identique à celle de Equals.

Le compilateur C# émet un avertissement si votre code enfreint cette règle.

Comment corriger les violations

Pour corriger une violation de cette règle, vous devez supprimer l'implémentation de l'opérateur d'égalité ou substituer Equals et faire en sorte que les deux méthodes retournent les mêmes valeurs. Si l'opérateur d'égalité n'introduit aucun comportement incohérent, vous pouvez corriger la violation en fournissant une implémentation de Equals qui appelle la méthode Equals dans la classe de base.

Quand supprimer les avertissements

Il est possible de supprimer sans risque un avertissement de cette règle si l'opérateur d'égalité retourne la même valeur que l'implémentation héritée de Equals. La section Exemple inclut un type qui pourrait supprimer sans risque un avertissement de cette règle.

Exemples de définitions d'égalité incohérentes

Description

L'exemple suivant montre un type avec des définitions d'égalité incohérentes. BadPoint modifie la signification d'égalité en fournissant une implémentation personnalisée de l'opérateur d'égalité, mais ne substitue pas Equals afin qu'il se comporte de la même manière.

Code

using System;

namespace UsageLibrary
{   
    public class BadPoint
    {
        private int x,y, id;
        private static int NextId;

        static BadPoint()
        {
            NextId = -1;
        }
        public BadPoint(int x, int y)
        {
            this.x = x;
            this.y = y;
            id = ++(BadPoint.NextId); 
        }

        public override string ToString()
        {
            return String.Format("([{0}] {1},{2})",id,x,y);
        }

        public int X {get {return x;}}

        public int Y {get {return x;}}
        public int Id {get {return id;}}

        public override int GetHashCode()
        {
            return id;
        }
        // Violates rule: OverrideEqualsOnOverridingOperatorEquals.

        // BadPoint redefines the equality operator to ignore the id value.
        // This is different from how the inherited implementation of 
        // System.Object.Equals behaves for value types. 
        // It is not safe to exclude the violation for this type. 
        public static bool operator== (BadPoint p1, BadPoint p2)
        {
            return ((p1.x == p2.x) && (p1.y == p2.y));
        }
        // The C# compiler and rule OperatorsShouldHaveSymmetricalOverloads require this.
        public static bool operator!= (BadPoint p1, BadPoint p2)
        {
            return !(p1 == p2);
        }
    }
}

Exemple

Le code suivant teste le comportement de BadPoint.

using System;

namespace UsageLibrary
{   
    public class TestBadPoint
    {
        public static void Main()
        {
            BadPoint a = new BadPoint(1,1);
            BadPoint b = new BadPoint(2,2);
            BadPoint a1 = a;
            BadPoint bcopy = new BadPoint(2,2);

            Console.WriteLine("a =  {0} and b = {1} are equal? {2}", a, b, a.Equals(b)? "Yes":"No");
            Console.WriteLine("a == b ? {0}", a == b ? "Yes":"No");
            Console.WriteLine("a1 and a are equal? {0}", a1.Equals(a)? "Yes":"No");
            Console.WriteLine("a1 == a ? {0}", a1 == a ? "Yes":"No");

            // This test demonstrates the inconsistent behavior of == and Object.Equals.
            Console.WriteLine("b and bcopy are equal ? {0}", bcopy.Equals(b)? "Yes":"No");
            Console.WriteLine("b == bcopy ? {0}", b == bcopy ? "Yes":"No");
        }
    }
}

Cet exemple génère la sortie suivante :

                

L'exemple suivant présente un type qui techniquement viole cette règle, mais ne se comporte pas de façon incohérente.

using System;

namespace UsageLibrary
{
    public struct GoodPoint
    {
        private int x,y;

        public GoodPoint(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public override string ToString()
        {
            return String.Format("({0},{1})",x,y);
        }

        public int X {get {return x;}}

        public int Y {get {return x;}}

        // Violates rule: OverrideEqualsOnOverridingOperatorEquals,
        // but does not change the meaning of equality;
        //  the violation can be excluded.

        public static bool operator== (GoodPoint px, GoodPoint py)
        {
            return px.Equals(py);
        }

        // The C# compiler and rule OperatorsShouldHaveSymmetricalOverloads require this.
        public static bool operator!= (GoodPoint px, GoodPoint py)
        {
            return !(px.Equals(py));
        }
    }
}

Le code suivant teste le comportement de GoodPoint.

using System;

namespace UsageLibrary
{ 
    public class TestGoodPoint
    {
        public static void Main()
        {
            GoodPoint a = new GoodPoint(1,1);
            GoodPoint b = new GoodPoint(2,2);
            GoodPoint a1 = a;
            GoodPoint bcopy = new GoodPoint(2,2);

            Console.WriteLine("a =  {0} and b = {1} are equal? {2}", a, b, a.Equals(b)? "Yes":"No");
            Console.WriteLine("a == b ? {0}", a == b ? "Yes":"No");
            Console.WriteLine("a1 and a are equal? {0}", a1.Equals(a)? "Yes":"No");
            Console.WriteLine("a1 == a ? {0}", a1 == a ? "Yes":"No");

            // This test demonstrates the consistent behavior of == and Object.Equals.
            Console.WriteLine("b and bcopy are equal ? {0}", bcopy.Equals(b)? "Yes":"No");
            Console.WriteLine("b == bcopy ? {0}", b == bcopy ? "Yes":"No");
        }
    }
}

Cet exemple génère la sortie suivante :

                

L'exemple suivant résout la violation en substituant Object.Equals.

using System; 

namespace Samples
{    
    public class Point    
    {        
        private readonly int _X;        
        private readonly int _Y;         

        public Point(int x, int y)        
        {            
            _X = x;            
            _Y = y;        
        }         

        public int X        
        {            
            get { return _X; }        
        }         

        public int Y        
        {            
            get { return _Y; }        
        }         

        public override int GetHashCode()        
        {            
            return _X ^ _Y;        
        }         

        public override bool Equals(object obj)        
        {            
            if (obj == null)                
                return false;             

            if (GetType() != obj.GetType())                
                return false;             

            Point point = (Point)obj;             

            if (_X != point.X)                
                return false;             

            return _Y == point.Y;        
        }         

        public static bool operator ==(Point point1, Point point2)        
        {            
            return Object.Equals(point1, point2);        
        }         

        public static bool operator !=(Point point1, Point point2)        
        {            
            return !Object.Equals(point1, point2);        
        }    
    }
}

L'exemple suivant résout la violation en substituant ValueTypeEquals().

using System; 

namespace Samples
{    
    public struct Point : IEquatable<Point>    
    {        
        private readonly int _X;        
        private readonly int _Y;         

        public Point(int x, int y)        
        {            
            _X = x;            
            _Y = y;        
        }         

        public int X        
        {            
            get { return _X; }        
        }         

        public int Y        
        {            
            get { return _Y; }        
        }         

        public override int GetHashCode()        
        {            
            return _X ^ _Y;        
        }         

        public override bool Equals(object obj)        
        {            
            if (!(obj is Point))                
                return false;             

            return Equals((Point)obj);        
        }         

        public bool Equals(Point other)        
        {            
            if (_X != other._X)                
                return false;             

            return _Y == other._Y;        
        }         

        public static bool operator ==(Point point1, Point point2)        
        {            
            return point1.Equals(point2);        
        }         

        public static bool operator !=(Point point1, Point point2)        
        {            
            return !point1.Equals(point2);        
        }    
    }
}

Exemple de classe

Description

L'exemple suivant indique une classe (type référence) qui enfreint cette règle.

Code

using System; 

namespace Samples
{    
    // Violates this rule    
    public class Point    
    {        
        private readonly int _X;        
        private readonly int _Y;         

        public Point(int x, int y)        
        {            
            _X = x;            
            _Y = y;        
        }         

        public int X        
        {            
            get { return _X; }        
        }         

        public int Y        
        {            
            get { return _Y; }        
        }         

        public override int GetHashCode()        
        {            
            return _X ^ _Y;        
        }             

        public static bool operator ==(Point point1, Point point2)        
        {            
            if (point1 == null || point2 == null)                
                return false;             

            if (point1.GetType() != point2.GetType())                
                return false;             

            if (point1._X != point2._X)                    
                return false;             

            return point1._Y == point2._Y;        
        }         

        public static bool operator !=(Point point1, Point point2)        
        {            
            return !(point1 == point2);        
        }    
    }
}

Exemple de structure

Description

L'exemple suivant indique une structure (type valeur) qui enfreint cette règle.

Code

using System; 

namespace Samples
{    
    // Violates this rule    
    public struct Point    
    {        
        private readonly int _X;        
        private readonly int _Y;         

        public Point(int x, int y)        
        {            
            _X = x;            
            _Y = y;        
        }         

        public int X        
        {            
            get { return _X; }        
        }         

        public int Y        
        {            
            get { return _Y; }        
        }         

        public override int GetHashCode()        
        {            
            return _X ^ _Y;        
        }         

        public static bool operator ==(Point point1, Point point2)        
        {            
            if (point1._X != point2._X)                
                return false;                        

            return point1._Y == point2._Y;        
        }         

        public static bool operator !=(Point point1, Point point2)        
        {            
            return !(point1 == point2);        
        }    
    }
}

Règles connexes

CA1046 : Ne pas surcharger l'opérateur égal à sur les types référence

CA2225 : Les surcharges d'opérateur offrent d'autres méthodes nommées

CA2226 : Les opérateurs doivent contenir des surcharges symétriques

CA2218 : Remplacez GetHashCode au moment de remplacer Equals

CA2231 : Surchargez l'opérateur égal (equals) en remplaçant ValueType.Equals