System.Object

System.Object is the ubiquitous base type. All classes and structures inherit from System.Object, either directly or indirectly. System.Object encompasses the baseline behavior of all managed types. Reference types without an explicit base class inherit from System.Object implicitly. Reference types that explicitly inherit from another type still inherit from System.Object, but indirectly. You can inherit from System.Object explicitly. However, that is somewhat redundant and therefore is not often seen. Value types inherit from System.ValueType, which in turn inherits from System.Object. System.ValueType overrides System.Object members to implement the semantics of a value type.

The members of the System.Object class are explained in Table 3-2.

Table 3-2. System.Object methods

Method

Description

Constructor

public Object( )

This is the default constructor of the System.Object class. It is called whenever an instance of any object is created. The object keyword is an alias for the System.Object type.

Equals

public virtual bool Equals(object obj)

public static bool Equals(object obj1, object obj2)

Equals returns true if the value of two objects is equal (in the case of the single-argument form, the value of obj is compared to the instance on which the method is called). For reference types, identities are compared. For value types, the values are compared, which is an equivalency test. This method can be overridden to implement a custom comparison.

Finalize

protected virtual void Finalize()

In the Finalize method, cleanup for unmanaged resources takes place. Finalize is called during garbage collection. In C#, the destructor is equivalent to the Finalize method. Destructors and the finalization process are discussed in Chapter 18.

GetHashCode

public virtual int GetHashCode()

This method returns the hash code of an instance. The default hash code is not guaranteed to be unique or to have even distribution. You can override this method to return a meaningful hash code for a derived type or provide better distribution. For example, the Employee class could override GetHashCode to return the employee ID as the hash code.

GetType

public Type GetType()

GetType returns a Type reference, which is useful for examining a type at runtime. This is called reflection. Reflection is discussed in Chapter 13.

MemberwiseClone

protected object MemberwiseClone()

This method returns a cloned instance of the current object. It performs a shallow copy. Members that are reference types in the original object will point to the same objects as members in the cloned object, which can lead to side effects.

ReferenceEquals

public static bool ReferenceEquals(object obj1, object obj2)

This method returns true if the identity of two objects is the same.

ToString

public virtual string ToString()

This method returns a string representation of the current instance. The default result is the type name of the current instance. ToString is frequently overridden to return useful information about an instance. For the Employee class, ToString could be overridden to return the employee name.

Object.Equals Method

For reference types, the Object.Equals method compares identity. In the default implementation of this method, references are equal when pointing to the same object; references that point to different objects are not equal even if they have the same state. You can override the Equals method to perform a value comparison. For value types, the Equals method is already overridden to compare values.

In Applied Microsoft .NET Framework Programming (Microsoft Press, 2001), author Jeffrey Richter mentions four tenets of the Equals method: reflexivity, symmetry, transitivity, and consistency. These rules are important when overriding the Equals method. Breaking one of these rules can cause unexpected results.

  • Reflexivity. An object is always equal to itself. The obj1.Equal(obj1) call always must return true.

  • Symmetry. If obj1.Equals(obj2) is true, then obj2.Equals(obj1) also must be true.

  • Transitivity. If obj1.Equals(obj2) and obj2.Equals(obj3) are both true, then obj3.Equals(obj1) also must be true.

  • Consistency. If obj1.Equals(obj2) is true, then obj1.Equals(obj2) must always be true as long as the state of neither object has changed.

This code shows the override of the Equals method in the Employee class:

public static bool operator==(Employee obj1, Employee obj2) {
    return obj1.Equals(obj2);
}

public static bool operator!=(Employee obj1, Employee obj2) {
    return !obj1.Equals(obj2);
}

public override bool Equals(object obj) {
    Employee _obj = obj as Employee;

    if (obj == null) {
        return false;
    }
    return this.GetHashCode()==_obj.GetHashCode();
}

The preceding code overrides the Equals, operator==, and operator!= methods. The default implementation of these operators will not call the overridden Equals method. This can cause inconsistencies, where the comparison operators behave differently from the explicit Equals method. For this reason, both operators have been overridden.

When overriding the Equals method, you also should override the GetHashCode method. If you do not, a compiler warning occurs. Objects that are equal should have the same hash code, so that equality can be based on comparing hash codes. In the Equals method, call GetHashCode to retrieve and compare hash codes.

Object.GetHashCode Method

GetHashCode returns a hash code as an integer. Override this method to return a unique identifier for an instance. As indicated in the previous section, the Equals and GetHashCode methods should be implemented in tandem.

The data used to calculate a hash code should be constant. If not, when the data changes, the hash code also changes. This could cause problems, particularly if the hash code is used as a key in a collection. If the key dynamically changes, the original key is stale. Stale keys can cause conflicts in a collection. For this reason, data related to the hash code should not change.

The following code overrides the GetHashCode method for the Employee class. The EmplID field used for the hash is read-only. After the Employee instance is created, EmplID cannot be modified. This adheres to the requirement that the hash code be constant. There are a variety of algorithms for creating efficient and distributed hash codes—some quite complex. For simplicity, in the following example, GetHashCode simply returns the EmplID property:

public override int GetHashCode() {
    return EmplID;
}

Object.GetType Method

The GetType method returns a Type object. This Type instance can be used to perform reflection on the related object. Members of the Type class, such as GetMethods, GetFields, and other similar methods, return information on the object.

The following code demonstrates reflection. In the code, the public methods of System.Object are enumerated and displayed. As expected, the methods of Table 3-2 are displayed. The exception is the Object.MemberwiseClone method, which is not a public method:

using System;
using System.Collections;
using System.Reflection;

namespace Donis.CSharpBook {

    public class Starter {
        public static void Main() {
            Object obj = new Object();
            Type t = obj.GetType();
            foreach (MethodInfo m in t.GetMethods()) {
                Console.WriteLine(m.Name);
            }
        }
    }
}

Object.ToString Method

The ToString method returns a string representation of an instance. The default return value is the fully qualified name of the type. The ToString method of a primitive value type is already overridden to display a string representation of the value. The following code displays the default string representation of a value type and of a reference type:

int locala = 10;
Console.WriteLine(locala.ToString());  // Writes 10
Object obj = new Object();
Console.WriteLine(obj.ToString());     // Writes System.Object

For the Employee class, ToString could be overridden to return the name of the employee:

public override string ToString() {
  return FullName;
}

Object.MemberwiseClone Method

MemberwiseClone returns a new instance of an object. A shallow copy is performed. An object is cloned by performing a bitwise copy of each member. If a member is a value type, the values are copied and there is no side effect. For a member that is a reference type, the member reference—not the member object—is copied. The result is that the reference members of the new instance are aliases for the corresponding members in the original object, as shown in Figure 3-2. Changes in the objects referenced by these members will affect the same objects for both the original and cloned object.

The result of MemberwiseClone

Figure 3-2. The result of MemberwiseClone

MemberwiseClone is protected and cannot be overridden. The method is called from a derived class when implementing the ICloneable interface. The ICloneable interface defines an interface for cloning objects. The only member is the ICloneable.Clone method.

The following code shows the implementation of the ICloneable.Clone method in the Employee class. To clone an object, the MemberwiseClone method is called in ICloneable.Clone. In the Employee class (shown in full in the section titled "Employee Class," later in this chapter), propFirst and propLast are a member and a reference type. Cloning the object would copy the references of propFirst and propLast but not their values. In Main, obj2 is cloned from obj1. The hash of both objects confirms different identities. Nonetheless, changes to the employee name of one object affects both, which is a nasty side effect of MemberwiseClone. Both objects reference the same name in memory:

using System;
public class Starter {
    public static void Main() {
        Employee obj1 = new Employee(5678);
        Employee obj2 = (Employee) obj1.Clone();
        obj1.Last = "Marshall";
        obj2.First = "Donis";
         Console.WriteLine("Obj1 HC " +
            obj1.GetHashCode().ToString());
        Console.WriteLine(obj1.EmplID + ": " + obj1.FullName); // 5678: Donis Marshall
        Console.WriteLine("Obj2 HC " +
            obj2.GetHashCode().ToString());
        Console.WriteLine(obj2.EmplID + ": " + obj2.FullName); // 5678: Donis Marshall
    }
}

class Employee : ICloneable {

    public Employee(int id) {
        if ((id < 1000) || (id > 9999)) {
            throw new Exception(
                "Invalid Employee ID");
        }

        propID = id;

    }

    public object Clone() {
        return MemberwiseClone();
    }
//  end of partial listing...

Object.ReferenceEquals Method

The ReferenceEquals method compares identity. If the objects are the same, ReferenceEquals returns true. Otherwise, false is returned. ReferenceEquals is not virtual and cannot be overridden in the derived class. The following code compares an original object and a cloned object, as detailed in the section titled "Object.MemberwiseClone Method," later in this chapter. The objects have the same state but different identities. Because the identities are distinct, the ReferenceEquals method returns false:

Employee obj1 = new Employee(5678);
Employee obj2 = (Employee) obj1.Clone();
if (Employee.ReferenceEquals(obj1, obj2)) {
    Console.WriteLine("objects identical");
}
else {
    Console.WriteLine("objects not identical");
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.160.43