Home > Archive > C# > May 2005 > Newbie question about object comparisons
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Newbie question about object comparisons
|
|
| damiensawyer@yahoo.com.au 2005-05-19, 8:56 am |
| Hello,
I've recently transfered from vb.net to c#. I'm witnessing some
confusing behaviour that I'm hoping someone will be able to explain to
me.
In the following code, both of these variables return a value of type
'object'.
* ((iOPWLifecycle)value).Key
* key
At runtime, both of the above contain matching string objects, "abc",
however the following code snippent is evaluating them as not equal.
if (((iOPWLifecycle)value).Key != key)
{
// the thread is stepping into the exception code.
throw new Exception("Expected key match");
}
However, if I change the code to the following, the code runs as I
expected, only raising the error when the values are different.
if (!((iOPWLifecycle)value).Key.Equals(key))
{
throw new Exception("Expected key match");
}
In the first example, if I explicity cast both of the values to
'string' objects, the code works as expected.
Whilst I've found a workaround, I am thoroughly as to why c#
behaves as such. If both variables return string objects, why doesn't
c# correctly apply the default string comparison?
If someone could point me in the right direction with this, I would
greatly appreciate it. I can see how not understanding this properly
would be asking for a stack of 'nasty' bugs.
Thanks in advance,
Damien Sawyer
| |
|
|
<damiensawyer@yahoo.com.au> wrote in message
news:1116479397.907615.43230@g43g2000cwa.googlegroups.com...
> Hello,
>
> I've recently transfered from vb.net to c#. I'm witnessing some
> confusing behaviour that I'm hoping someone will be able to explain to
> me.
>
> In the following code, both of these variables return a value of type
> 'object'.
> * ((iOPWLifecycle)value).Key
> * key
>
> At runtime, both of the above contain matching string objects, "abc",
> however the following code snippent is evaluating them as not equal.
>
> if (((iOPWLifecycle)value).Key != key)
> {
> // the thread is stepping into the exception code.
> throw new Exception("Expected key match");
> }
>
> However, if I change the code to the following, the code runs as I
> expected, only raising the error when the values are different.
>
> if (!((iOPWLifecycle)value).Key.Equals(key))
> {
> throw new Exception("Expected key match");
> }
>
>
> In the first example, if I explicity cast both of the values to
> 'string' objects, the code works as expected.
>
>
> Whilst I've found a workaround, I am thoroughly as to why c#
> behaves as such. If both variables return string objects, why doesn't
> c# correctly apply the default string comparison?
>
> If someone could point me in the right direction with this, I would
> greatly appreciate it. I can see how not understanding this properly
> would be asking for a stack of 'nasty' bugs.
>
> Thanks in advance,
>
>
>
> Damien Sawyer
>
I cannot get a code example that works the way you describe. I tried adding
the items to a hashtable, casting to objects, looping through, creating new
objects that have the string "abc" in them, all evaluate to being equal as
expected. Can you produce a simple example that demonstrates the behaviour
you mention?
Lorad
| |
| Bruce Wood 2005-05-19, 8:58 pm |
| This happens because of the way that overloads are handled versus the
way that overrides are handled.
"Overload" means that there are several method with the same name and
different parameter lists. The != operator is defined many times for
many different types, the only difference being that the parameter
types (left-hand-side, right-hand-side) are different in each case. The
!= operator cannot possibly be overridden because it's static, and
static classes, methods, and properties don't participate in
inheritance.
"Override" means that a child class has provided a new implementation
for a virtual method defined farther up the class hierarchy. The Equals
method is overridden in the String class (since it was originally
defined in the Object class).
Here is the critical concept: which method _overload_ to call is
determined by the compiler. Which version of an _overridden_ method to
call is determined at run time.
So, here is some code to illustrate:
string temp = "a";
string a = "abc";
string b = temp + "b" + "c";
object o = a;
object p = b;
if (o != p) ...
At this point, the compiler must decide which version of != to call,
since the choice between various != operators is based on method
overloading. The compiler _has no idea_ what type of object o and p
refer to. Remember, this is not run time, when o and p point to real,
instantiated objects in memory. This is compile time, and all the
compiler knows is that o is an object and p is an object. So, it calls
the method (optimized to a single instruction, but conceptually it's a
method):
public static bool operator != (object lhs, object rhs)
The implementation of != provided by Object is to compare addresses.
Since o and p contain different addresses*, they compare as not equal.
However, if you say:
if (!o.Equals(p))
you are now calling an virtual method, Equals, which is overridden in
String. The compiler decides to call the version of Equals for the
object o that takes another Object as its argument:
public bool Equals(object otherObj)
but leaves the decision as to which Equals method to call (from which
level in the object hierarchy) until run time, because it's a virtual
method. At run time, o and p point to actual, isntantiated objects
(they're Strings), so the version of Equals that ends up being called
is the String.Equals method that takes an Object as its only argument.
This method compares two strings as strings, and so returns true
because both strings contain "abc" even though they are stored in
different places in memory.
* Probably the reason that Lorad couldn't reproduce this behaviour is
because of "string interning". If you write code like this:
string a = "abc";
stirng b = "abc";
object o = a;
object p = b;
if (o != p) ...
Then the != operator will, in fact, compare o and p as equal. This is
because the compiler recognizes that both a and b point to the same
string, "abc", and so it will point both of them to the same address in
memory. This in turn means that o and p will contain the same address,
which means that they will compare equal, even though the comparison is
an address comparison and not comparing strings.
In order to get two strings containing "abc" that "live" in different
places in memory, you have to calculate at least one of them, so that
the compiler doesn't clue in to what's going on and intern them.
| |
| Bruce Wood 2005-05-19, 8:58 pm |
| Incidentally, the way to fix the code so that it works properly every
time and is easier to read, too, is like this:
string lifeCycleKey = (string)((iOPWLifecycle)value).Key;
string keyString = (string)key;
if (lifeCycleKey != keyString)
{
// the thread is stepping into the exception code.
throw new Exception("Expected key match");
}
Now the compiler knows that you are using != to compare two strings,
and will invoke the string comparison operator, which will compare the
actual string contents rather than their addresses. It's easier to
read, too.
| |
| damiensawyer@yahoo.com.au 2005-05-20, 8:57 am |
| Hi Bruce,
Thanks for an extremely comprehensive and informative answer! You have
completely removed my confusion!
Out of interest, I did actually explicitly cast the values to strings
and, as expected after your explanation, it worked. However, in the
business problem I'm trying to solve, the 'keys' are not always
strings. Indeed, I've got half a dozen custom objects that need to be
compared.
Anyway - thanks again for your detailed answer. I think that C# is
slowly growing on me :-)
DS
| |
| Bruce Wood 2005-05-20, 8:57 pm |
| You have probably already figured this out, but for the benefit of such
posterity as there is in a usenet group :-)
> However, in the business problem I'm trying to solve, the 'keys' are
not always
> strings. Indeed, I've got half a dozen custom objects that need to be
compared.
In that case I amend my recommended solution: use the Equals method.
The CLR will figure out (via virtual methods and overriding) which is
the correct Equals to call to compare your two keys.
|
|
|
|
|