Explanation of object equality.

D

DamienS

In the interests of me saving hair, can someone please explain to me
what's going on below? Why doesn't == work in comparing two int's when
cast as objects? They're the same type.

Note that it worked for strings.

Thanks in advance,


Damien


---------------
using System;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
test();
}

static public void test()
{
object o1 = 5;
object o2 = 5;
Console.WriteLine(o1==o2); // returns false - why is this?
Console.WriteLine(o1.Equals(o2)); // returns true

// Check they're the same type
Console.WriteLine(o1.GetType().FullName); // Returns
System.Int32
Console.WriteLine(o2.GetType().FullName); // Returns
System.Int32

// try specifically casting
Console.WriteLine(Convert.ToInt32(o1) ==
Convert.ToInt32(o2)); // returns true

// How about with strings?
object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true
Console.WriteLine(o3.Equals(o4)); // returns true

Console.ReadLine();
}
}
}
 
J

Jeff Louie

Damien... Be careful. As Jon has pointed out in the past, the
overloaded
string equality operator only works on reference variables of Type
string.

object a= "Hello";
object b= "Hello";
bool isSameReference= (a == b); // test if a and b contain references to
the
same object or to no object (are both null)
bool isSameContent= ((string)a == (string)b); // test if string
referenced by a
and string referenced b have the same content/value or a and b are both
null

Session["KEY"]="VALUE"; // Error! The left hand operand is of type
object!
This is a reference based comparison. Do this:
(String)Session["KEY"]="VALUE"; // content equivalence

Regards,
Jeff
object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true<<
 
M

Moe Sisko

DamienS said:
Note that it worked for strings.

// How about with strings?
object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true
Console.WriteLine(o3.Equals(o4)); // returns true

Even though it worked for strings in your example, note that it may not
always work in the way you expect.

e.g. :
==
object o3 = "hello";
object o4 = "hello";
object o5 = "hello".ToLower();

Console.WriteLine(o3 == o4); // true
Console.WriteLine(Object.ReferenceEquals(o3, o4)); // true
Console.WriteLine(String.Equals(o3, o4)); // true

Console.WriteLine(o3 == o5); // false !!!!
Console.WriteLine(Object.ReferenceEquals(o3, o5)); // false
Console.WriteLine(String.Equals(o3, o5)); // true
==

Note the result of (o3 == o5) is false, because the references of the two
strings are different. It happens to return true in the (o3 == o4) test
because the two references happens to be the same.
 
M

Moe Sisko

Peter Duniho said:
Because the String class == operator overload does essentially call the
Equals() method.

Peter,

Note that in the example shown by the op :

object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true

that the String class == operator is *not* being called.
It just happens to return true because the references to o3 and o4 are the
same.
 
A

Anthony Jones

Moe Sisko said:
Peter,

Note that in the example shown by the op :

object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true

that the String class == operator is *not* being called.
It just happens to return true because the references to o3 and o4 are the
same.

It is true that the references are the same since the compilier creates only
one "hello" string. However the strings == overload will surely be called
what other choice is there? Its quite likely that the overload will do a
reference equality comparison first.
 
A

Anthony Jones

Anthony Jones said:
It is true that the references are the same since the compilier creates
only one "hello" string. However the strings == overload will surely be
called what other choice is there? Its quite likely that the overload
will do a reference equality comparison first.

Oops stratch that! The variable types are object. There is no way the
string == overload will ever be called. Its too early in morning :(
 
M

Marc Gravell

However the strings == overload will surely be
called what other choice is there?

The alternative is a referential equality check; since the operands are
"object", and operator overloads are determined by static analysis -
this is what happens. To demonstrate:

object o3 = "hello";
string s = "hel";
if (o3 != null) s += "lo";
object o4 = s;
// now o3 and o4 contain "hello"
Console.WriteLine(o3 == o4); // returns false

For info, this is implemented as a direct reference test:

ldloc.0 // o3
ldloc.2 // o4
ceq // ==

Marc
 
A

Anthony Jones

Marc Gravell said:
The alternative is a referential equality check; since the operands are
"object", and operator overloads are determined by static analysis - this
is what happens. To demonstrate:

object o3 = "hello";
string s = "hel";
if (o3 != null) s += "lo";
object o4 = s;
// now o3 and o4 contain "hello"
Console.WriteLine(o3 == o4); // returns false

For info, this is implemented as a direct reference test:

ldloc.0 // o3
ldloc.2 // o4
ceq // ==

Yes, sorry Marc for wasting your time, I was half asleep.
 
M

Moe Sisko

Peter Duniho said:
I never said it was. In fact, the whole point of my post was to point out
that it's not.

I don't disagree with what you wrote, but I don't understand why you are
replying to my post simply to reiterate what I wrote.

Pete

Pete,

It's just the way I interpreted your post, in the context of the op's
original post.

The op's original post said :
==
Note that it worked for strings.
....
.... ** then later down, this is his supporting evidence for "it worked for
strings" **

// How about with strings? ** Code Snippet X **
object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true
Console.WriteLine(o3.Equals(o4)); // returns true
==
To which you replied :
==
Note that it worked for strings.

Because the String class == operator overload does essentially call the
Equals() method.
==

Your explanation for "why it worked for strings", sounds like its saying
that "code snippet X" is causing the string class == operator overload to be
invoked. Or at least that's how I (wrongly) interpreted your post.

Sorry if there was any misunderstanding.

Moe.
 
G

Göran Andersson

Peter said:
I never said it was.

Yes, you did.

You said:

"Because the String class == operator overload does essentially call the
Equals() method."

The reason that the object references are compared is _not_ that the
string class == operator calls the Equals method. The reason that the
object references are compared is because the two variables that are
compared are two object references. It's the object class == operator
that is being called, not the string class == operator.
 
J

Jeff Louie

Damien... Did we answer your question? You asked:

// How about with strings?
object o3 = "hello";
object o4 = "hello";
Console.WriteLine(o3 == o4); // returns true

Strings are a very special case. First, when both operands are of type
String,
the overloaded equality operator checks for content equivalence. In your
example however, the operands are of type object, not String, so the
object
equality operator is called, not the overloaded string equality
operator. The
object equality operator checks to see if o3 and o4 both contain
references to
the same object or are both null.

Second, unlike most objects, string storage is more fully optimized.
Strings
are shared or interned so that the o3 and o4 variables both contain
references to a single interned string.

"The common language runtime conserves string storage by maintaining a
table, called the intern pool, that contains a single reference to each
unique
literal string declared or created programmatically in your program.
Consequently, an instance of a literal string with a particular value
only exists
once in the system."

http://msdn.microsoft.com/en-us/library/system.string.intern.aspx

Due this optimization or interning, the o3 and o4 variables contain
references
to the same string object and the call returns true.

At this risk of telling you "more than you really wanted to know", most
objects, not just strings, _are_ optimized to some extent. Each instance
of a
class may contain methods and fields, but the compiler is able to
optimize
the code in memory, so that instance methods, but not fields, are shared
to
reduce memory requirements.

Regards,
Jeff
 
D

DamienS

G'day everyone and thanks for all your posts. My apologies for the
delayed reply.
Damien... Did we answer your question? You asked:

Look, to tell you the truth, I was thouroughly confused until your
explanation Jeff of interned strings. I've never heard of them. It's
far from it being "more than I wanted to know" - in fact, I guess
that's exactly the answer I was looking for when I posted the
question; namely an explanation of what I saw was an apparent
inconsistency. So - thanks for that :)

For what it's worth, I do think that there's a language inconsitency
in that, for the following, Result=true:
object o1 = "hello";
object o2 = "hello";
bool Result = o1.Equals(o2);

Whilst Interning explains it perfectly, I don't necessary agree with
the design of the language in permitting it, as it introduces
inconsistent behaviour. Surely the "interning engine" in the compiler
could determine that the objects were allocated differently and were
possibly intended by the devloper to indeed 'be' different, whilst
still maintaining the memory efficiency behind the scenes. I think
that the current set up is a prone to bugs ('twas a bug which spawned
this thread in the first place). Anyway - my opinions are neither here
nor there and largely inconsequential. In any case, it's definitely a
trap for young players :)

Thanks very much to all who posted. I especially enjoyed the "You
said... No I didn't... yes you did..." argument :)



Damien.
 
J

Jon Skeet [C# MVP]

G'day everyone and thanks for all your posts. My apologies for the
delayed reply.


Look, to tell you the truth, I was thouroughly confused until your
explanation Jeff of interned strings. I've never heard of them. It's
far from it being "more than I wanted to know" - in fact, I guess
that's exactly the answer I was looking for when I posted the
question; namely an explanation of what I saw was an apparent
inconsistency. So - thanks for that :)

For what it's worth, I do think that there's a language inconsitency
in that, for the following, Result=true:
object o1 = "hello";
object o2 = "hello";
bool Result = o1.Equals(o2);

Whilst Interning explains it perfectly

No, interning isn't really relevant in the above. The above will call
the *overridden* method in string.

Interning is only relevant when you're using ==, where the compiler
just uses the normal object definition, i.e. reference equality. This
is the big difference between overriding and overloading - overriding
is an execution time decision based on the actual type of the object.
Overloading is a compile time decision based on the declared types of
the arguments/operands.
I don't necessary agree with
the design of the language in permitting it, as it introduces
inconsistent behaviour. Surely the "interning engine" in the compiler
could determine that the objects were allocated differently and were
possibly intended by the devloper to indeed 'be' different, whilst
still maintaining the memory efficiency behind the scenes.

How could it determine that the objects were allocated "differently"?
They're allocated in the same way in two different lines, that's all.

I don't see any problem with string interning being in the language -
so long as it's well documented (which it is in the C# spec).

Jon
 

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