Share via


CA2224:オーバーロードする演算子 equals で Equals をオーバーライドします

アイテム [値]
規則 ID CA2224
カテゴリ Microsoft.Usage
互換性に影響する変更点 なし

原因

パブリック型で等値演算子が実装されていますが、System.Object.Equals がオーバーライドされていません。

規則の説明

等値演算子は、Equals メソッドの機能に構文的に便利な方法でアクセスすることが意図されています。 等値演算子を実装する場合、そのロジックは Equals のそれと同じにする必要があります。

コードがこの規則に違反した場合、C# コンパイラから警告が発行されます。

違反の修正方法

この規則違反を修正するには、等値演算子の実装を削除するか、Equals をオーバーライドし、2 つのメソッドに同じ値を返させます。 等値演算子で不整合な動作が発生しない場合、基底クラスで Equals を呼び出す Equals を実装することで違反を修正できます。

どのようなときに警告を抑制するか

等値演算子から Equals の継承実装と同じ値が返される場合、この規則からの警告を抑制しても問題ありません。 この記事の例では、この規則からの警告を抑制しても問題ない型が含まれています。

一貫性のない等値定義の例

次の例は、等値の定義に一貫性がない型を示しています。 BadPoint は、等値演算子のカスタム実装を提供することで等値の意味を変更しますが、動作が同じになるように Equals をオーバーライドしません。

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

次のコードでは、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");
        }
    }
}

この例を実行すると、次の出力が生成されます。

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

次の例は、技術的にはこの規則に違反していますが、一貫性のない方法で動作していない型を示しています。

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

次のコードでは、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");
        }
    }
}

この例を実行すると、次の出力が生成されます。

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

クラスの例

次の例では、この規則に違反するクラス (参照型) を確認できます。

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

次の例では、System.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);        
        }    
    }
}

構造体の例

次の例は、この規則に違反する構造体 (値型) を示しています。

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

次の例では、System.ValueType.Equals をオーバーライドすることで違反が修正されています。

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:参照型で、演算子 equals をオーバーロードしないでください

CA2225:演算子オーバーロードには名前付けされた代替が存在します

CA2226:演算子は対称型オーバーロードを含まなければなりません

CA2218:オーバーライドする Equals で GetHashCode をオーバーライドします

CA2231:ValueType.Equals のオーバーライドで、演算子 equals をオーバーロードします