Comparing Objects For Equality


After discussing operators and briefly touching on the equality operator, it is worth considering for a moment what equality means when dealing with instances of classes and structs. Understanding the mechanics of object equality is essential for programming logical expressions and is important when implementing operator overloads and casts, which is the topic of the rest of this chapter.

The mechanisms of object equality are different depending on whether you are comparing reference types (instances of classes), or value types (the primitive data types, instances of structs or enums). The following sections look at the equality of reference and value types independently.

Comparing Reference Types for Equality

One aspect of System.Object that can look surprising at first sight is the fact that it defines three different methods for comparing objects for equality: ReferenceEquals() and two versions of Equals(). Add to this the comparison operator (==), and you actually have four ways of comparing for equality. Some subtle differences exist between the different methods, which are examined next.

The ReferenceEquals() method

ReferenceEquals()is a static method that tests whether two references refer to the same instance of a class; specifically whether the two references contain the same address in memory. As a static method, it is not possible to override, so the System.Object implementation is what you always have. ReferenceEquals() will always return true if supplied with two references that refer to the same object instance, and false otherwise. It does, however, consider null to be equal to null:

 SomeClass x, y; x = new SomeClass(); y = new SomeClass(); bool B1 = ReferenceEquals(null, null);     // returns true bool B2 = ReferenceEquals(null,x);         // returns false bool B3 = ReferenceEquals(x, y);           // returns false because x and y // point to different objects 

The virtual Equals() method

The System.Object implementation of the virtual version of Equals() also works by comparing references. However, because this method is virtual, you can override it in your own classes in order to compare objects by value. In particular, if you intend instances of your class to be used as keys in a dictionary, you will need to override this method to compare values. Otherwise, depending on how you override Object.GetHashCode(), the dictionary class that contains your objects will either not work at all or will work very inefficiently. One point you should note when overriding Equals() is that your override should never throw exceptions. Once again, this is because doing so could cause problems for dictionary classes and possibly certain other .NET base classes that internally call this method.

The static Equals() method

The static version of Equals() actually does the same thing as the virtual instance version. The difference is that the static version takes two parameters and compares them for equality. This method is able to cope when either of the objects is null, and therefore, provides an extra safeguard against throwing exceptions if there is a risk that an object might be null. The static overload first checks whether the references it has been passed are null. If they are both null, it returns true (because null is considered to be equal to null). If just one of them is null, it returns false. If both references actually refer to something, it calls the virtual instance version of Equals(). This means that when you override the instance version of Equals(), the effect is as if you were overriding the static version as well.

Comparison operator (==)

The comparison operator can be best seen as an intermediate option between strict value comparison and strict reference comparison. In most cases, writing

 bool b = (x == y); // x, y object references 

means that you are comparing references. However, it is accepted that there are some classes whose meanings are more intuitive if they are treated as values. In those cases, it is better to override the comparison operator to perform a value comparison. Overriding operators is discussed next, but the obvious example of this is the System.String class for which Microsoft has overridden this operator to compare the contents of the strings rather than their references.

Comparing Value Types for Equality

When comparing value types for equality, the same principles hold as for reference types: Reference Equals() is used to compare references, Equals() is intended for value comparisons, and the comparison operator is viewed as an intermediate case. However the big difference is that value types need to be boxed in order to convert them to references so that methods can be executed on them. In addition, Microsoft has already overloaded the instance Equals() method in the System.ValueType class in order to test equality appropriate to value types. If you call sA.Equals(sB) where sA and sB are instances of some struct, the return value will be true or false according to whether sA and sB contain the same values in alltheir fields. On the other hand, no overload of == is available by default for your own structs. Writing (sA == sB) in any expression will result in a compilation error unless you have provided an overload of== in your code for the struct in question.

Another point is that ReferenceEquals()always returns false when applied to value types, because to call this method, the value types will need to be boxed into objects. Even if you write

 bool b = ReferenceEquals(v,v); // v is a variable of some value type 

you will still get the answer of false because v will be boxed separately when converting each parameter, which means you get different references. Calling ReferenceEquals() to compare value types doesn't really make much sense.

Although the default override of Equals() supplied by System.ValueType will almost certainly be adequate for the vast majority of structs that you define, you might want to override it again for your own structs in order to improve performance. Also, if a value type contains reference types as fields, you might want to override Equals() to provide appropriate semantics for these fields, because the default override of Equals() will simply compare their addresses.




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net