Matthew Layton
Matthew Layton

Reputation: 42260

IEquatable<T> - Best practice override for .Equals(object obj) in C#

Whenever I write a new class or struct that is likely to hold some data, that may need to be compared, I always implement IEquatable<T> as this provides the class/struct with a strongly typed .Equals(T other) method.

example:

public struct Radius : IEquatable<Radius>
{
    public Int32 TopLeft { get; set; }
    public Int32 TopRight { get; set; }
    public Int32 BottomLeft { get; set; }
    public Int32 BottomRight { get; set; }

    public bool Equals(Radius other)
    {
        return this.TopLeft == other.TopLeft
            && this.TopRight == other.TopRight
            && this.BottomLeft == other.BottomLeft
            && this.BottomRight == other.BottomRight;
    }
}

As well as providing an implementation for .Equals(Radius other), I should really override the default implementation too (.Equals(object obj))

I have two options here, and my question is, which of these implementations is better?

Option 1 is to use casting:

public override bool Equals(object obj)
{
    return this.Equals((Radius)obj);
}

Option 2 is to use the "as" keyword:

public override bool Equals(object obj)
{
    return this.Equals(obj as Radius);
}

My reason for asking this is, using casting will throw an exception if obj cannot be cast to Radius, whereas as will resolve to null if it cannot be cast, therefore it just checks this against null, without throwing an exception; So is it better to throw an exception, or to just return false?

EDIT: As it has been pointed out by quite a few fellow SO'ers, structs cannot be null, therefore the second option does not apply for a struct. Therefore another question springs to mind: Should the overridden implementation of .Equals(object obj) be identical for structs and classes?

Upvotes: 4

Views: 4652

Answers (3)

Oliver
Oliver

Reputation: 45101

As @SLaks already mentioned Equals() should never throw.

In this special case i think a usage of the is operator combined with a cast should help you out:

public override bool Equals(object obj)
{
     if(obj is Radius)
         return Equals((Radius)obj);

     return false;
}

In cases where you have a class you should simply use the as operator:

public override bool Equals(object obj)
{
     return Equals(obj as MyObj);
}

public bool Equals(MyObj obj)
{
     if(ReferenceEquals(obj, null))
         return false;

     // ToDo: further checks for equality.
}

Upvotes: 5

SLaks
SLaks

Reputation: 887433

The Equals() method must never throw an exception.

An object of a different type is merely unequal.

Quoting the documentation:

Implementations of Equals must not throw exceptions; they should always return a value. For example, if obj is null, the Equals method should return false instead of throwing an ArgumentNullException.

Upvotes: 7

chris warner
chris warner

Reputation: 178

My personal opinion would be to use the second option, or even check before hand if the object is a "Radius" and then return false so that the intent is more clear

Upvotes: -1

Related Questions