Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why cant we override Equals() in a value type without boxing?

Tags:

c#

I know I can avoid boxing by adding my own Equals implementation.

public struct TwoDoubles
{
    public double m_Re;
    public double m_Im;

    public TwoDoubles(double one, double two)
    {
        m_Re = one;
        m_Im = two;
    }

    public override bool Equals(object ob)
    {
           return Equals((TwoDoubles)ob);
    }
    public bool Equals(TwoDoubles ob)
    {
        TwoDoubles c = ob;
        return m_Re == c.m_Re && m_Im == c.m_Im;
    }
}

I can't call this an override as much as an overload. By the magic of the runtime it does correctly call the correct Equals() implementation based on the type of the caller.

Why can't I override and change the parameter type to TwoDoubles and let boxing occur by the power of the runtime on an as needed basis? Is it because C# doesn't support parameter contravariance (if that's the reason then why is it not supported...seems a small step from object o = new TwoDoubles())?

UPDATE
Clarification: object is a part of the inheritance hierarchy of a struct. Why can we not specify a more derived type as a parameter to override an implementation from a less derived type? This would allow us to write:

 public override bool Equals(TwoDoubles ob)
 {
        TwoDoubles c = ob;
        return m_Re == c.m_Re && m_Im == c.m_Im;    
 }

Which should be called when the variable is a TwoDouble even if said variable has been boxed into an object type.

like image 476
P.Brian.Mackey Avatar asked Apr 30 '12 21:04

P.Brian.Mackey


People also ask

Can the equals method be overridden?

We can override the equals method in our class to check whether two objects have same data or not.

Why do we need to override the equals () method of the object class?

Why we override equals() method? It needs to be overridden if we want to check the objects based on the property. For example, we want to check the equality of employee object by the id. Then, we need to override the equals() method.

Why override GetHashCode when Equals method is overridden?

Why is it important to override GetHashCode ? It s important to implement both equals and gethashcode, due to collisions, in particular while using dictionaries. if two object returns same hashcode, they are inserted in the dictionary with chaining. While accessing the item equals method is used.

What happens if we don't override equals method?

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object. hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.


2 Answers

Why can't I override and change the parameter type to TwoDoubles?

Because that would not be typesafe!

class B
{
  public virtual void M(Animal animal) { ... }
}
class D : B
{
  public override void M(Giraffe animal) { ... }
}

B b = new D();
b.M(new Tiger());

And now you just passed a tiger to a method that actually only takes a giraffe!

Same thing in your case. You're overriding a method that takes any object with a method that can only take a struct; that's not typesafe.

Is it because C# doesn't support parameter type contravariance?

No, it is because you are asking for parameter type covariance, which is not typesafe.

C# does not support parameter type contravariance either, but that's not what you're asking for.

like image 113
Eric Lippert Avatar answered Sep 18 '22 02:09

Eric Lippert


You can change the parameter (overload) for Equals, just as you've done, and boxing will occur as needed (i.e. whenever Equals(object) is called)

Because everything inherits (or implicitly via boxing) from object, you cannot stop people from being able to use Equals(object) on your type. However, by doing so, you get a boxed call.

If you are in control of the calling code, then always use your new overloaded option, i.e. Equals(TwoDouble)

Note, as one commentor already said, your code is slightly incorrect, do this instead:

public override bool Equals(object ob)
{
  if (ob is TwoDoubles)
    return Equals((TwoDoubles)ob);
  else
    return false;
}
public bool Equals(TwoDoubles c)
{
  return m_Re == c.m_Re && m_Im == c.m_Im;
}

EDIT as has been wisely suggested, you should accomplish this same task but by using the IEquatable interface, in this case IEquatable<TwoDouble> (note, no code change necessary from what we've done as it matches the signature)

like image 31
payo Avatar answered Sep 17 '22 02:09

payo