Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Object Equality and Identity

Frequently, developers write code to compare objects with one another. This is particularly true when placing objects in a collection and you’re writing code to sort, search, or compare items in a collec- tion. In this section, I'll discuss object equality and identity, and I’ll also discuss how to define a type that properly implements object equality.

The System.Object type offers a virtual method named Equals, whose purpose is to return true if two objects contain the same value. The implementation of Object’s Equals method looks like this.

 

public class Object {

public virtual Boolean Equals(Object obj) {

 

// If both references point to the same object,

// they must have the same value. if (this == obj) return true;

 

// Assume that the objects do not have the same value. return false;

}

}

 

At first, this seems like a reasonable default implementation of Equals: it returns true if the this and obj arguments refer to the same exact object. This seems reasonable because Equals knows that an object must have the same value as itself. However, if the arguments refer to different objects, Equals can’t be certain if the objects contain the same values, and therefore, false is returned. In other words, the default implementation of Object’s Equals method really implements identity, not value equality.

Unfortunately, as it turns out, Object’s Equals method is not a reasonable default, and it should have never been implemented this way. You immediately see the problem when you start thinking about class inheritance hierarchies and how to properly override Equals. Here is how to properly implement an Equals method internally:

1.If the obj argument is null, return false because the current object identified by this is obviously not null when the nonstatic Equals method is called.

2.If the this and obj arguments refer to the same object, return true. This step can improve

performance when comparing objects with many fields.


3.If the this and obj arguments refer to objects of different types, return false. Obviously, checking if a String object is equal to a FileStream object should result in a false result.

4.For each instance field defined by the type, compare the value in the this object with the value in the obj object. If any fields are not equal, return false.

5.Call the base class’s Equals method so it can compare any fields defined by it. If the base

class’s Equals method returns false, return false; otherwise, return true.

 

So Microsoft should have implemented Object’s Equals like this.

 

public class Object {

public virtual Boolean Equals(Object obj) {

// The given object to compare to can't be null if (obj == null) return false;

 

// If objects are different types, they can't be equal. if (this.GetType() != obj.GetType()) return false;

 

// If objects are same type, return true if all of their fields match

// Because System.Object defines no fields, the fields match return true;



}

}

 

But, because Microsoft didn’t implement Equals this way, the rules for how to implement Equals are significantly more complicated than you would think. When a type overrides Equals, the over- ride should call its base class’s implementation of Equals unless it would be calling Object’s imple- mentation. This also means that because a type can override Object’s Equals method, this Equals method can no longer be called to test for identity. To fix this, Object offers a static Reference­ Equals method, which is implemented like this.

 

public class Object {

public static Boolean ReferenceEquals(Object objA, Object objB) { return (objA == objB);

}

}

 

You should always call ReferenceEquals if you want to check for identity (if two references point to the same object). You shouldn’t use the C# == operator (unless you cast both operands to Object first) because one of the operands’ types could overload the == operator, giving it semantics other than identity.

As you can see, the .NET Framework has a very confusing story when it comes to object equal- ity and identity. By the way, System.ValueType (the base class of all value types) does override Object’s Equals method and is correctly implemented to perform a value equality check (not an identity check). Internally, ValueType’s Equals is implemented this way:

1.If the obj argument is null, return false.

2.If the this and obj arguments refer to objects of different types, return false.


3.For each instance field defined by the type, compare the value in the this object with the value in the obj object by calling the field’s Equals method. If any fields are not equal, return false.

4.Return true. Object’s Equals method is not called by ValueType’s Equals method.

 

Internally, ValueType’s Equals method uses reflection (covered in Chapter 23, “Assembly Loading and Reflection”) to accomplish step 3. Because the CLR’s reflection mechanism is slow, when defining your own value type, you should override Equals and provide your own implementation to improve the performance of value equality comparisons that use instances of your type. Of course, in your own implementation, do not call base.Equals.

When defining your own type, if you decide to override Equals, you must ensure that it adheres to the four properties of equality:

■ Equals must be reflexive; that is, x.Equals(x) must return true.

 

■ Equals must be symmetric; that is, x.Equals(y) must return the same value as

y.Equals(x).

 

■ Equals must be transitive; that is, if x.Equals(y) returns true and y.Equals(z) returns

true, then x.Equals(z) must also return true.

 

■ Equals must be consistent. Provided that there are no changes in the two values being com- pared, Equals should consistently return true or false.

If your implementation of Equals fails to adhere to all of these rules, your application will behave in strange and unpredictable ways.

When overriding the Equals method, there are a couple more things that you’ll probably want to do:

Have the type implement the System.IEquatable<T> interface’s Equals methodThis generic interface allows you to define a type-safe Equals method. Usually, you’ll implement the Equals method that takes an Object parameter to internally call the type-safe Equals method.

Overload the == and != operator methodsUsually, you’ll implement these operator meth- ods to internally call the type-safe Equals method.

 

Furthermore, if you think that instances of your type will be compared for the purposes of sort- ing, you’ll want your type to also implement System.IComparable’s CompareTo method and System.IComparable<T>’s type-safe CompareTo method. If you implement these methods, you’ll also want to overload the various comparison operator methods (<, <=, >, >=) and implement these methods internally to call the type-safe CompareTo method.



Date: 2016-03-03; view: 668


<== previous page | next page ==>
Nbsp;   Boxing and Unboxing Value Types | Nbsp;   Object Hash Codes
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.007 sec.)