ReferenceEquals

  • Thread starter Thread starter Daniel Billingsley
  • Start date Start date
D

Daniel Billingsley

In C# these are equivalent, right?

if (ReferenceEquals(objectA, objectB))
{
}

and

if (objectA == objectB)
{
}
 
In C# these are equivalent, right?
if (ReferenceEquals(objectA, objectB))
{
}

and

if (objectA == objectB)
{
}

The method System.Object.ReferenceEquals() always compares references.
Although a class can provide its own behavior for the equality operator
(below), that re-defined operator isn't invoked if the operator is called
via a reference to System.Object. Given this class:

public class EvenOdd
{
public int val;
public static bool operator==(EvenOdd left, EvenOdd right)
{
return left.val % 2 == right.val % 2;
}
public static bool operator!=(EvenOdd left, EvenOdd right)
{
return ! (left == right);
}
}

You'll get different results, depending on whether you invoke the comparison
via a reference to EvenOdd or a reference to System.Object.

If you create two EvenOdd instances:

EvenOdd a = new EvenOdd();
EvenOdd b = new EvenOdd();
a.val = 2;
b.val = 4;

Then call this method:

static void TestObject(object thing1, object thing2)
{
Console.WriteLine("Reference equals: {0}",
object.ReferenceEquals(thing1, thing2));

Console.WriteLine("Equality operator: {0}", thing1==thing2);
}

False is displayed twice to the console.

However, for a slightly different case:

static void TestEvenOdd(EvenOdd thing1, EvenOdd thing2)
{
Console.WriteLine("Reference equals: {0}",
object.ReferenceEquals(thing1, thing2));

Console.WriteLine("Equality operator: {0}", thing1 == thing2);
}

False is printed for the first case, and True for the second.

You might want to avoid this problem by overriding the virtual Equals method
if your redefinition of equality is meant to be true even when accessed via
a base type reference:

// EvenOdd, version 2
public class EvenOdd
{
public int val;
public static bool operator==(EvenOdd left, EvenOdd right)
{
return left.val % 2 == right.val % 2;
}
public static bool operator!=(EvenOdd left, EvenOdd right)
{
return ! (left == right);
}

public override bool Equals(object obj)
{
// Error-handling removed...
return ((EvenOdd)obj).val % 2 == val % 2;
}
}

With the Equals method, the proper equality comparison can be made through
any type of reference. This method displays False, then True (as expected):
static void TestEquals(object thing1, object thing2)
{
Console.WriteLine("Reference equals: {0}",
object.ReferenceEquals(thing1, thing2));

Console.WriteLine("Equals method: {0}", thing1.Equals(thing2));
}
 
Daniel Billingsley said:
In C# these are equivalent, right?

if (ReferenceEquals(objectA, objectB))
{
}

and

if (objectA == objectB)
{
}

Only if objectA and objectB are variables declared to be type object or
some other type which doesn't override ==. For instance, with strings,

if (stringA==stringB)

is equivalent to

if (String.Equals(stringA, stringB))
 
Only if objectA and objectB are variables declared to be type object or
some other type which doesn't override ==. For instance, with strings,

if (stringA==stringB)

is equivalent to

if (String.Equals(stringA, stringB))

It doesn't matter so much what the type of the object is, as much as the
type of reference to the object (or maybe that's what you meant). It gets
really tricky with edge cases for strings. If the strings are tested via a
reference to object rather than string, then Equals and == can return
different values.

Because they aren't interned, the two following string instances have two
separate references to two separate objects, and return true if compared
with the Equals method or the == operator via a string reference:
string x = new string(new char[]{'f', 'o', 'o'});
string y = new string(new char[]{'f', 'o', 'o'});

// Displays False, True, True
Console.WriteLine("Reference equals: {0}",
object.ReferenceEquals(x, y));
Console.WriteLine("Equality operator: {0}", x==y);
Console.WriteLine("Equals method: {0}", x.Equals(y));

Compare those two strings via reference to object, and you get different
results.

Of course, if you're sure that the strings are interned, then they will
share a common pooled instance:
string a = "bar";
string b = "bar";
// True
Console.WriteLine("Reference equals: {0}",
object.ReferenceEquals(a, b));

This is a degenerate case that could be somewhat mitigated by explicitly
interning the strings as they are composed. In practice though, there's no
way to be 100% sure that an alledgedly cooperating assembly is causing
strings to be interned while composing.
 
It doesn't matter so much what the type of the object is, as much as the
type of reference to the object (or maybe that's what you meant).

Yes - that's why I specifically used which type the *variables* are
declared to be, rather than which type the objects are :)
 
Thanks everyone. It seems I always get carried away trying to be brief and
go too far.

I am talking specifically where objectA and objectB are declared as and
refer to a class that I've written which doesn't override == or Equals().

It was actually while looking at some sample code from another person, and I
just thought == seemed more straightforward. No, wait a minute, the
original code was in VB.NET. Given the differences between Is in the two
languages, and between = (VB) and == (C#) I guess ReferenceEquals() is a
nice consistent way of getting exactly what was desired - to see if the
variables refer to the same object.
 
Back
Top