Equals

K

Kurt

Wouldn't you agree all of the follwoing should produce the same
result?

r = (o1 == o2);
r = (o2 == o1);
r = object.Equals(o1, o2);
r = object.Equals(o2, o1);
r = (o1.Equals(o2));
r = (o2.Equals(o1));

However if either the objects are null as an exception will be thrown
using one of the last two statements. However if the idea was to make
every object an object why doesn't null implement object? I
understand why this happens as there is no vtable yet why shouldn't
the runtime implement a Equals method for null rather then throwing an
exception? Then every object would truely be an object rather then
every object, providing its is not null, implements Equals.
Is it just me that thinks this way? Does the 2.0 version also have
this problem?

- Kurt
 
J

Jeff Louie

Kurt ... As you have discovered, although this call looks object
oriented, it
fails in real world use. It is there for convenience, since coders
expect the old
convention to exist. Equal(x,y) is the practical approach. So Bertrand
Meyers
argues that the convention x.twin should be replaced by clone(x) and
x.is_equal(y) is to be replaced by equal(x,y). OO Software Construction
v2.0
p274-275.

Regards,
Jeff
r = (o1.Equals(o2));
r = (o2.Equals(o1));
 
J

Jon Skeet [C# MVP]

Kurt said:
Wouldn't you agree all of the follwoing should produce the same
result?
No.

r = (o1 == o2);
r = (o2 == o1);

These are comparing references, assuming o1 and o2 are declared as
object.
r = object.Equals(o1, o2);
r = object.Equals(o2, o1);
r = (o1.Equals(o2));
r = (o2.Equals(o1));

These will all use the potentially overridden version of Equals.
However if either the objects are null as an exception will be thrown
using one of the last two statements. However if the idea was to make
every object an object why doesn't null implement object?

Because null isn't an object - it's a reference which is to no object.
I understand why this happens as there is no vtable yet why shouldn't
the runtime implement a Equals method for null rather then throwing an
exception?

Because Equals is just like every other method - calling it involves
dereferencing, which you can't do with null.
Then every object would truely be an object rather then
every object, providing its is not null, implements Equals.
Is it just me that thinks this way? Does the 2.0 version also have
this problem?

It's not a problem - it's the way it's meant to be. You shouldn't be
trying to dereference null.
 
K

Kurt

Jon Skeet said:
Theorically equals should be a symetrical operation (docs state among
others the following should be true for every implementation of
equals)
x.Equals(a null reference (Nothing)) returns false.
x.Equals(y) returns the same value as y.Equals(x).

Therefore it is not symetrical when y == null

A more practical example:
if hashtable.contains(name) and value is a boolean and value equals
true return true else return false could be written as:
return true.Equals(hashtable[name]);
but cannot be written as:
hastable[name].Equals(true);
You could also use object.Equals(true, hashtable[name]).
But the operator == will cause a compiler warning because object and
true are different types. You can cast true as object but not
hastable[name] to (bool) as you may recieve an invalid cast exception
if value is not typeof boolean.
These are comparing references, assuming o1 and o2 are declared as
object.


These will all use the potentially overridden version of Equals.


Because null isn't an object - it's a reference which is to no object.

I was thinking along the lines of the NullObject pattern where there
would be no such thing as no object. "null" should be static instance
such as:
static Object.Null = new NullObject();
Because Equals is just like every other method - calling it involves
dereferencing, which you can't do with null.

See above
It's not a problem - it's the way it's meant to be. You shouldn't be
trying to dereference null.

Okay its not a "problem" but theorically they should be symetrical and
IMHO I shouldn't need to worry about it. I think there is a better
solution. The technical difficulty however is that null can be
assigned to any type. However this didn't stop them from developing
transparent proxies so I don't see why the runtime couldn't generate
an appropriate vtable when one attempts to dereference null as they do
when a transparent proxy is cast to another interface, the generated
vtable would implement object methods and by default throw an
exception for user specific methods.

vtable for normal instance of Foo (created when constructed)
Equals = Object.Equals()
Bar = Foo.Bar()
...

vtable for null instances of Foo (generated when null cast to Foo):
this = Object.Null
Equals = Object.Equals()
Bar = Object.ThrowNullException()


Perhaps taking this further perhaps a special operator could be used
to define the null instance implementation if desired.

class Foo {
public void Bar () { ... }
static Foo _null = new NullFoo();
// special null operator
public static operator null() { return _null; }
class NullFoo { public void Bar() { ... } }
}

now the vtable for null instance could look like this:
this = Foo.Null
Equals = Object.Equals()
Bar = NullFoo.Bar()
...


For interfaces however things become less clear what null
implementation would you like to use? the solution to that would to
use the static method for the desired class.
IFoo f = Foo.Null; // use foo null implemetation - perhaps does
nothing
f = Object.Null; // throws exception when calling Bar

The default implementation of operator null if not explicitly defined
as above would then be public static operator null() { return
Object.Null; }

- Kurt
 
J

Jon Skeet [C# MVP]

Kurt said:
Theorically equals should be a symetrical operation (docs state among
others the following should be true for every implementation of
equals)
x.Equals(a null reference (Nothing)) returns false.
x.Equals(y) returns the same value as y.Equals(x).

Therefore it is not symetrical when y == null

You missed part of the documentation:

<quote>
In the list, x, y, and z represent object references that are not a
null reference (Nothing in Visual Basic).
A more practical example:
if hashtable.contains(name) and value is a boolean and value equals
true return true else return false could be written as:
return true.Equals(hashtable[name]);
but cannot be written as:
hastable[name].Equals(true);

Can't say I've ever seen that usage pattern, and it seems a very bad
one to me, as it creates loads of boxed booleans. More common is to use
hashtable[name]=name;

and then just use hashtable.ContainsKey(name)
You could also use object.Equals(true, hashtable[name]).
But the operator == will cause a compiler warning because object and
true are different types. You can cast true as object but not
hastable[name] to (bool) as you may recieve an invalid cast exception
if value is not typeof boolean.

So avoid the pattern...
I was thinking along the lines of the NullObject pattern where there
would be no such thing as no object. "null" should be static instance
such as:
static Object.Null = new NullObject();

While that's an appropriate pattern in some situations, I don't
personally think it should be used for *everything*. For a start, it
means you often wouldn't get an error when you're incorrectly trying to
use something which *shouldn't* be null.
See above

I guess you just disagree with the design philosophy of nulls in .NET -
but it's a fairly common philosophy, and one which certainly isn't
going to change.
Okay its not a "problem" but theorically they should be symetrical

The docs don't say that.
and IMHO I shouldn't need to worry about it. I think there is a better
solution.

I disagree. I *like* seeing errors when I try to dereference null. I
usually don't want it to just not be equal to things.
The technical difficulty however is that null can be
assigned to any type. However this didn't stop them from developing
transparent proxies so I don't see why the runtime couldn't generate
an appropriate vtable when one attempts to dereference null as they do
when a transparent proxy is cast to another interface, the generated
vtable would implement object methods and by default throw an
exception for user specific methods.

<snip>

There are certainly technical ways of achieving it - but we disagree
about whether or not it would be better than the current system.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top