CA2224:重载相等运算符时重写 Equals 方法
类型名 |
OverrideEqualsOnOverloadingOperatorEquals |
CheckId |
CA2224 |
类别 |
Microsoft.Usage |
是否重大更改 |
否 |
原因
某公共类型实现了等号运算符,但是没有重写 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);
}
}
}
下面的示例通过重写 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);
}
}
}
类示例
说明
下面的示例演示一个与此规则冲突的类(引用类型)。
代码
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);
}
}
}