CA2224: 같음 연산자를 오버로드할 때 Equals를 재정의하십시오.
TypeName |
OverrideEqualsOnOverloadingOperatorEquals |
CheckId |
CA2224 |
범주 |
Microsoft.Usage |
변경 수준 |
주요 변경 아님 |
원인
public 형식이 같음 연산자를 구현하지만 Object.Equals를 재정의하지 않습니다.
규칙 설명
같음 연산자는 간편한 구문을 통해 Equals 메서드의 기능에 액세스할 수 있도록 제공됩니다. 같음 연산자를 구현할 경우 해당 논리는 Equals의 논리와 같아야 합니다.
코드에서 이 규칙을 위반하면 C# 컴파일러에서 경고를 발생시킵니다.
위반 문제를 해결하는 방법
이 규칙 위반 문제를 해결하려면 같음 연산자의 구현을 제거하거나, Equals를 재정의하고 두 메서드에서 같은 값을 반환하도록 해야 합니다. 같음 연산자가 일관된 동작을 나타내면 기본 클래스의 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");
}
}
}
이 예제의 결과는 다음과 같습니다.
다음 예제에서는 기술적으로 이 규칙을 위반하지만 일관된 방식으로 동작하는 형식을 보여 줍니다.
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");
}
}
}
이 예제의 결과는 다음과 같습니다.
다음 예제에서는 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);
}
}
}
다음 예제에서는 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);
}
}
}
클래스 예제
설명
다음 예제에서는 이 규칙을 위반하는 클래스(참조 형식)를 보여 줍니다.
코드
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);
}
}
}
구조체 예제
설명
다음 예제에서는 이 규칙을 위반하는 구조체(값 형식)를 보여 줍니다.
코드
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);
}
}
}
관련 규칙
CA1046: 참조 형식에 같음 연산자를 오버로드하지 마십시오.
CA2225: 연산자 오버로드에는 명명된 대체 항목이 있습니다.
CA2226: 연산자에는 대칭 오버로드가 있어야 합니다.