What's your approach on writing equality checks for the structs
and classes
you create?
1) Does the "full" equality checking require that much of boilerplate code (like override Equals
, override GetHashCode
, generic Equals
, operator==
, operator!=
)?
2) Do you specify explicitly that your classes model the IEquatable<T>
interface?
3) Do I understand correctly, that ther开发者_运维技巧e is no actual way to automatically apply Equals
overrides, when I invoke something like a == b
and I always have to implement both the Equals
and operator==
members?
You're right, this is a lot of boiler-plate code, and you need to implement everything separately.
I would recommend:
- If you're going to implement value equality at all, override
GetHashCode
andEquals(object)
- creating overloads for == and implementingIEquatable<T>
without doing that could result in very unexpected behaviour - I would always implement
IEquatable<T>
if you're overridingEquals(object)
andGetHashCode
- I only overload the == operator more rarely
- Implementing equality correctly for unsealed classes is tricky, and can still produce surprising/undesirable results. If you need equality for types in a hierarchy, implement
IEqualityComparer<T>
expressing the comparison you're interested in. - Equality for mutable types is usually a bad idea, as two objects can be equal and then unequal later... if an object is mutated (in an equality-affecting way) after it's been used as a key in a hash table, you won't be able to find it again.
- Some of the boiler-plate is slightly different for structs... but like Marc, I very rarely write my own structs.
Here's a sample implementation:
using System;
public sealed class Foo : IEquatable<Foo>
{
private readonly string name;
public string Name { get { return name; } }
private readonly int value;
public int Value { get { return value; } }
public Foo(string name, int value)
{
this.name = name;
this.value = value;
}
public override bool Equals(object other)
{
return Equals(other as Foo);
}
public override int GetHashCode()
{
int hash = 17;
hash = hash * 31 + (name == null ? 0 : name.GetHashCode());
hash = hash * 31 + value;
return hash;
}
public bool Equals(Foo other)
{
if ((object) other == null)
{
return false;
}
return name == other.name && value == other.value;
}
public static bool operator ==(Foo left, Foo right)
{
return object.Equals(left, right);
}
public static bool operator !=(Foo left, Foo right)
{
return !(left == right);
}
}
And yes, that's a heck of a lot of boilerplate, very little of which changes between implementations :(
The implementation of ==
is slightly less efficient than it might be, as it will call through to Equals(object)
which needs to do the dynamic type check... but the alternative is even more boiler-plate, like this:
public static bool operator ==(Foo left, Foo right)
{
if ((object) left == (object) right)
{
return true;
}
// "right" being null is covered in left.Equals(right)
if ((object) left == null)
{
return false;
}
return left.Equals(right);
}
I rarely do anything special for classes; for most regular objects referential equality works fine.
I even more rarely write a struct
; but since structs represent values it is usually appropriate to provide equality etc. This would usually involve everything; Equals, ==, != and IEquatable<T>
(since this avoids boxing in scenarios using EqualityComparer<T>.Default
.
The boilerplate isn't usually too problematic, but IIRC tools like resharper can help here.
Yes, it is advisable to keep Equals and == in sync, and this needs to be done explicitely.
You just need to implement operator== for a==b .
As I like my data in dictionaries sometimes I override GetHashCode.
Next I implement Equals (as an unmentioned standard ... this is because there is no constraint for equality when using generics) and specify implementing IEquatable. Since I am going to do this I might as well point my == and != implementations to Equals. :)
See What is "Best Practice" For Comparing Two Instances of a Reference Type?
You can avoid boiler plate code (hope C#/VS team brings something easy for developers in their next iteration) with the help of a snippet, here is one such..
精彩评论