I'm working on a C# project on which, until now, I've used immutable objects and factories to ensure that objects of type Foo
can always be compared for equality with 开发者_高级运维==
.
Foo
objects can't be changed once created, and the factory always returns the same object for a given set of arguments. This works great, and throughout the code base we assume that ==
always works for checking equality.
Now I need to add some functionality that introduces an edge case for which this won't always work. The easiest thing to do is to overload operator ==
for that type, so that none of the other code in the project needs to change. But this strikes me as a code smell: overloading operator ==
and not Equals
just seems weird, and I'm used to the convention that ==
checks reference equality, and Equals
checks object equality (or whatever the term is).
Is this a legitimate concern, or should I just go ahead and overload operator ==
?
There's a big difference between overloading ==
and overriding Equals.
When you have the expression
if (x == y) {
The method that will be used to compare variables x and y is decided at compile time. This is operator overloading. The type used when declaring x and y is used to define which method is used to compare them. The actual type within x and y (i.e., a subclass or interface implementation) is irrelevant. Consider the following.
object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference
if (x == y) { // evaluates to FALSE
and the following
string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference
if (x == y) { // evaluates to TRUE
This demonstrates that the type used to declare the variables x and y is used to determine which method is used to evaluate ==.
By comparison, Equals is determined at runtime based on the actual type within the variable x. Equals is a virtual method on Object that other types can, and do, override. Therefore the following two examples both evaluate to true.
object x = "hello";
object y = 'h' + "ello"; // ensure it's a different reference
if (x.Equals(y)) { // evaluates to TRUE
and the following
string x = "hello";
string y = 'h' + "ello"; // ensure it's a different reference
if (x.Equals(y)) { // also evaluates to TRUE
I believe the standard is that for most types, .Equals checks object similarity, and operator ==
checks reference equality.
I believe best practice is that for immutable types, operator ==
should be checking for similarity, as well as .Equals
. And if you want to know if they really are the same object, use .ReferenceEquals
. See the C# String
class for an example of this.
It definitely smells. When overloading ==
you should make sure that both Equals()
and GetHashCode()
are also consistent. See the MSDN guidelines.
And the only reason that this seems OK at all is that you describe your type as immutable.
Sample showing how to implement this according to MSFT guidelines (below). Notice, when overriding Equals you also need to override GetHashCode(). Hope this helps folks.
public class Person
{
public Guid Id { get; private set; }
public Person(Guid id)
{
Id = id;
}
public Person()
{
Id = System.Guid.NewGuid();
}
public static bool operator ==(Person p1, Person p2)
{
bool rc;
if (System.Object.ReferenceEquals(p1, p2))
{
rc = true;
}
else if (((object)p1 == null) || ((object)p2 == null))
{
rc = false;
}
else
{
rc = (p1.Id.CompareTo(p2.Id) == 0);
}
return rc;
}
public static bool operator !=(Person p1, Person p2)
{
return !(p1 == p2);
}
public override bool Equals(object obj)
{
bool rc = false;
if (obj is Person)
{
Person p2 = obj as Person;
rc = (this == p2);
}
return rc;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
For immutable types I don't think there is anything wrong with having ==
overloaded to support value equality. I don't think I'd override ==
without overriding Equals
to have the same semantics however. If you do override ==
and need to check reference equality for some reason, you can use Object.ReferenceEquals(a,b)
.
See this Microsoft article for some useful guidelines
According to Microsofts own best practises the outcome of the Equals method and the equals (==) overload should be the same.
CA2224: Override equals on overloading operator equals
精彩评论