By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,630 Members | 1,174 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,630 IT Pros & Developers. It's quick & easy.

An IEquatable<T> object cast to IEquatable<explicittype> results in wrong Equals override called.

P: n/a
I've got an object, Person, that supports IEquatable<Person>. It
implements
bool Equals(Person obj)
as well as overriding
bool Equals(object obj)

I've got a container type that holds a member object of generic type
T, that supports IEquatable<T>, and a method, DoComparisons(T obj) to
compare the member object to the object passed in.

In the method, if I call:

member.Equals(obj);

Then the Equals(Person obj) overload is called,

but if I cast the member to IEquatable<Personand then call

((IEquatable<Person>)member).Equals(obj);

Then the Equals(object obj) overload is called.

My question is why? The type T should have been resolved to Person
when I created my container, why does the first call result in the
correct method being called on the interface, whereas when the object
is cast to IEquatable<Personit does not? Surely both should be doing
the same thing?

Here's the full code:
using System;
using System.Collections.Generic;
using System.Text;

namespace EquatableTest
{
public class Program
{
static void Main(string[] args)
{
Container<Personcontainer = new Container<Person>(new
Person("Gary Evans"));
Person person = new Person("Gary Evans");
container.DoComparisons(person);
}
}

public class Person : IEquatable<Person>
{
private string firstName;
private string lastName;

public Person(string fullName)
{
string[] names = fullName.Split(' ');
this.firstName = names[0];
this.lastName = names[names.Length - 1];
}

#region IEquatable<PersonMembers

public bool Equals(Person other)
{
Console.Write("Equals(Person other) called.");
return ToString() == other.ToString();
}

#endregion

public override string ToString()
{
return string.Concat(firstName, " ", lastName);
}

public override int GetHashCode()
{
return ToString().GetHashCode();
}

public override bool Equals(object obj)
{
Console.Write("Equals(object other) called.");
Person person = obj as Person;
return ((person != null) && (ToString() ==
person.ToString()));
}
}

public class Container<Twhere T : IEquatable<T>
{
private T member;
public Container(T t)
{
member = t;
}

public void DoComparisons(T obj)
{
Console.Write("Calling member.Equals() cast as
IEquatable<T>. ");
bool result = member.Equals(obj);
Console.WriteLine(" Result:" + result.ToString());

Console.Write("Calling member.Equals() cast as
IEquatable<Person>. ");
result = ((IEquatable<Person>)member).Equals(obj);
Console.WriteLine(" Result:" + result.ToString());
}
}
}

Cheers!
Gary

Apr 19 '07 #1
Share this Question
Share on Google+
5 Replies


P: n/a
You have
T obj;
blah
result = ((IEquatable<Person>)member).Equals(obj);
but it doesn't know (at compile time) that obj is a Person, so how
can it know to call .Equals(Person obj).

If you add a cast it might work:
result = ((IEquatable<Person>)member).Equals((Person)obj);

Likewise a constraint on T : Person

Just thoughts; none tested.

Marc
Apr 19 '07 #2

P: n/a
On Apr 19, 2:58 pm, "Marc Gravell" <marc.grav...@gmail.comwrote:
You have
T obj;
blah
result = ((IEquatable<Person>)member).Equals(obj);
but it doesn't know (at compile time) that obj is a Person, so how
can it know to call .Equals(Person obj).

If you add a cast it might work:
result = ((IEquatable<Person>)member).Equals((Person)obj);

Likewise a constraint on T : Person

Just thoughts; none tested.

Marc
Thanks for the reply.

Without casting any object everything works (the Equals(Person)
overload is called) - it doesn't know at compile time that obj is a
person, but at runtime the correct overload is correctly called as
inferred from the generic type.

What I'm wondering is, is why explicitly casting the member to Person
breaks this inferrence (of course I wouldn't cast anything to Person
in real life - that would nullify the point of me having a generic
class, I was just replicating the behaviour I saw in the watch window
whilst debugging a separate issue).

Cheers,
gary

Apr 19 '07 #3

P: n/a
it doesn't know at compile time that obj is a person,

It doesn't need to; it knows, however, that "member" is T, "obj" is T,
and that T : IEquatable<T>; hence "member.Equals(obj)" *can* (and
will) be used at compile-time to detect the Equals(T) option rather
than
Equals(obj). When you specified one (but not both) casts you broke
this relationship, and the only thing left was Equals(object).

For reference, this type of casting inside a generic essentially
defeats
the purpose of generics; likewise, it is recommended for
Equals(object)
to by functionally equivalent to Equals(T) for any supported T; for
simple
cases (only one T) you could just cast
bool override Equals(object obj) { return Equals((Person)obj);}
For more involved cases you may need to inspect obj to pick an
overload
manually.

Marc

Apr 20 '07 #4

P: n/a
On Apr 20, 5:21 am, Marc Gravell <marc.grav...@gmail.comwrote:
it doesn't know at compile time that obj is a person,

It doesn't need to; it knows, however, that "member" is T, "obj" is T,
and that T : IEquatable<T>; hence "member.Equals(obj)" *can* (and
will) be used at compile-time to detect the Equals(T) option rather
than
Equals(obj). When you specified one (but not both) casts you broke
this relationship, and the only thing left was Equals(object).

For reference, this type of casting inside a generic essentially
defeats
the purpose of generics; likewise, it is recommended for
Equals(object)
to by functionally equivalent to Equals(T) for any supported T; for
simple
cases (only one T) you could just cast
bool override Equals(object obj) { return Equals((Person)obj);}
For more involved cases you may need to inspect obj to pick an
overload
manually.

Marc
Hi Marc,

The penny's clicked with this issue now, I had a bit of a mental block
seeing that it was the cast that was forcing it to choose an overload
at compile time rather than at runtime. Thanks for spending your time
on this though!

About the casting defeating the point of generics - I agree! I'd never
put this in "real code" that's what I said as a note at the end of the
last post - I only put this in especially to replicate this issue (I
saw this behaviour originally in the watch window debugging a separate
issue, and wanted to replicate it). Similarly, I would have normally
implemented Equals(object) to return Equals(Person), but I thought I'd
keep them separate in this case to keep the console output cleaner.

Thanks again for your help!
Gary

Apr 20 '07 #5

P: n/a
No problem;
it was the cast that was forcing it to choose an overload
at compile time rather than at runtime
For regular code the overload is *always* decided at compile time;
otherwise you need to use reflection / dynamic invoke.

Marc
Apr 20 '07 #6

This discussion thread is closed

Replies have been disabled for this discussion.