CA2224: Equals beim Überladen von Gleichheitsoperatoren überschreiben.

Element Wert
RuleId CA2224
Category Microsoft.Usage
Unterbrechende Änderung Nicht unterbrechend

Ursache

Ein öffentlicher Typ implementiert den Gleichheitsoperator, setzt aber System.Object.Equals nicht außer Kraft.

Regelbeschreibung

Der Gleichheitsoperator ist als syntaktisch bequeme Methode für den Zugriff auf die Funktionalität der Equals-Methode gedacht. Wenn Sie den Gleichheitsoperator implementieren, muss seine Logik mit der von Equals identisch sein.

Der C#-Compiler gibt eine Warnung aus, wenn Ihr Code gegen diese Regel verstößt.

Behandeln von Verstößen

Um einen Verstoß gegen diese Regel zu beheben, sollten Sie entweder die Implementierung des Gleichheitsoperators entfernen oder Equals überschreiben und festlegen, dass die beiden Methoden dieselben Werte zurückgeben. Wenn der Gleichheitsoperator kein inkonsistentes Verhalten verursacht, können Sie den Verstoß beheben, indem Sie eine Implementierung von Equals bereitstellen, mit der die Equals-Methode in der Basisklasse aufgerufen wird.

Wann sollten Warnungen unterdrückt werden?

Eine Warnung dieser Regel kann sicher unterdrückt werden, wenn der Gleichheitsoperator denselben Wert zurückgibt wie die vererbte Implementierung von Equals. Die Beispiele in diesem Artikel zeigen einen Typ, mit dem eine Warnung aus dieser Regel sicher unterdrückt werden kann.

Beispiele für inkonsistente Gleichheitsdefinitionen

Das folgende Beispiel zeigt einen Typ mit inkonsistenten Gleichheitsdefinitionen. BadPoint ändert die Bedeutung von Gleichheit, indem eine benutzerdefinierte Implementierung des Gleichheitsoperators bereitgestellt wird, setzt aber Equals nicht außer Kraft, sodass sein Verhalten identisch ist.

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);
        }
    }
}

Der folgende Code testet das Verhalten von 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");
        }
    }
}

Dieses Beispiel erzeugt die folgende Ausgabe:

a =  ([0] 1,1) and b = ([1] 2,2) are equal? No
a == b ? No
a1 and a are equal? Yes
a1 == a ? Yes
b and bcopy are equal ? No
b == bcopy ? Yes

Das folgende Beispiel zeigt einen Typ, der technisch gegen diese Regel verstößt, sich aber nicht inkonsistent verhält.

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));
        }
    }
}

Der folgende Code testet das Verhalten von 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");
        }
    }
}

Dieses Beispiel erzeugt die folgende Ausgabe:

a =  (1,1) and b = (2,2) are equal? No
a == b ? No
a1 and a are equal? Yes
a1 == a ? Yes
b and bcopy are equal ? Yes
b == bcopy ? Yes

Klassenbeispiel

Das folgende Beispiel zeigt eine Klasse (Verweistyp), die gegen diese Regel verstößt.

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);        
        }    
    }
}

Im folgenden Beispiel wird der Verstoß durch das Überschreiben von System.Object.Equals korrigiert.

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);        
        }    
    }
}

Strukturbeispiel

Das folgende Beispiel zeigt eine Struktur (Werttyp), die gegen diese Regel verstößt:

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);        
        }    
    }
}

Im folgenden Beispiel wird der Verstoß durch das Überschreiben von System.ValueType.Equals korrigiert.

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);        
        }    
    }
}

CA1046: Gleichheitsoperator für Referenztypen nicht überladen.

CA2225: Operatorüberladungen weisen benannte Alternativen auf.

CA2226: Operatoren sollten symmetrische Überladungen aufweisen.

CA2218: GetHashCode beim Überschreiben von Equals überschreiben.

CA2231: Überladen Sie den Gleichheitsoperator beim Überschreiben von ValueType.Equals.