13.3. Object Class

In addition to the String class, there are several other classes that deserve special mention in the C# language—one such class is the Object class. The Object class is at the very root of the .NET class hierarchy. Every other .NET type, from the simple predefined value types, to strings, to arrays, to predefined reference types, to user-defined types, ultimately derives from the Object class.

Inheritance from the Object class is implicit; there is no need to include the syntax in a class definition:

: Object

That is, the class definition syntax

public class Student {...}

is equivalent to the more explicit

public class Student : Object {...}

The Object class is contained in the System namespace, but as with the String class there is an alias for the Object class—the keyword object (all lowercase)—which allows us to declare Object references without having to insert a using System; directive at the top of our program; the following two lines of code are thus equivalent:

System.Object x = y;

and

object x = y;

The Object class declares seven public methods that are inherited by, and hence available to, any object of any type. We'll discuss the two most commonly used Object methods in detail in the following sections: Equals and ToString.

13.3.1. Equals Method

The Equals method determines whether two object references are "equal." If the method is used to compare reference-type objects (for example, class instances or arrays), the Equals method determines whether the two references are referring to the exact same object in memory. If the method is applied to value-type objects (for example, primitive types such as int or float), Equals performs a bitwise comparison of the values of the objects. There are two versions of this method:

  • public virtual bool Equals(Object obj): This method can be called on any object, passing in a second object as an argument: if (x.Equals(y)) {...}.

  • public static bool Equals(Object objA, Object objB): This static method is called on the Object class, and the two references to be compared are both passed in as arguments to the method: if (Object.Equals(x, y)) {...}.

For example, let's create a Student object and maintain two references to it:

Student s1 = new Student("Fred");
Student s2 = s1;

Reference variables s1 and s2 thus reference the same object in memory. Now, let's create a second Student object with the same data values as the first:

Student s3 = new Student("Fred");

Using the "object as helium balloon" analogy, we created the situation portrayed in Figure 13-2.

Figure 13.2. Object #2 has the same data values as Object #1 but is a separate, distinct object in memory.

If we test for equality of s1 and s2 using the Equals method:

if (s1.Equals(s2)) {  // or, equivalently: Object.Equals(s1, s2);
  Console.WriteLine("s1 equals s2");
}
else {
  Console.WriteLine("s1 does not equal s2");
}

the output of this code would be as follows:

s1 equals s2

because s1 and s2 are indeed referencing the same object, whereas testing s1 and s3 for equality:

if (s1.Equals(s3)) {  // or, equivalently: Object.Equals(s1, s3);
  Console.WriteLine("s1 equals s3");
}

else {
  Console.WriteLine("s1 does not equal s3");
}

would generate the following output:

s1 does not equal s3

because despite the fact that s1 and s3 have the same data values (name is "Fred" in both cases), they are nonetheless references to distinct objects.

13.3.1.1. Overriding the Equals Method

The nonstatic version of the Object class's Equals method is declared to be virtual, allowing us to override its behavior in a derived class. We often override the Equals method to define a different interpretation of what it means for two objects to be equal: for example, in the Student class, we might want to deem two physically distinct Student objects as nonetheless being "equal" if they have the same value for their respective student ID numbers. To achieve this end, the Student class overrides the Equals method:

using System;

public class Student
{
  // Field.
  private string id;

  // Property.
  public string Id {
    // Accessor details omitted.
  }

  // Overriding the Equals method.
							public override bool Equals(object obj) {
    // Initialize a flag.
    bool isEqual = false;

    //  Use the as operator to try to cast the object
    //  argument into a Student object. This operator
    //  returns null if the cast was unsuccessful
    Student s = obj as Student;

    //  If the cast Student object reference is not null
    //  and if it has the same Id property value
    //  as the current Student object, set the isEqual
    //  flag to be true
    if ( s != null && s.Id == this.Id ) {
      isEqual = true;
    }

return isEqual;
  }
}

The overridden Equals method first uses the as operator to try to cast the object argument to the method into a Student. If the cast is unsuccessful, the operator returns null. If the cast Student object isn't null, and if the value of its Id property is equal to that of the current Student object, the isEqual flag is set to be true.

Let's put our new overridden method to work in our client code:

public class Example
{
  static void Main() {
    Professor p = new Professor();

    Student s1 = new Student(); // first object...
    s1.Id = "123-45-6789";

    Student s2 = new Student(); // second object...
    s2.Id = "123-45-6789"; // same ID as s1

    Student s3 = new Student(); // third object!
    s3.Id = "987-65-4321"; // different ID as s1 and s2

    Console.WriteLine("Is s1 equal to s2?  " + s1.Equals(s2));
							Console.WriteLine("Is s1 equal to s3?  " + s1.Equals(s3));
							Console.WriteLine("Is s1 equal to p?  " + s1.Equals(p));
  }
}

The preceding code would generate the following output:

Is s1 equal to s2?  True
Is s1 equal to s3?  False
Is s1 equal to p?  False

NOTE

Note that when we instead use the == operator to test two references for equality

if (x == y) {...}

the nature of the equality test is again class-dependent; if x and y are reference-type object references, then == tests to see if the two references are referring to the same physical object in memory. If x and y are value types, then == tests to see whether the two variables have the same values. For string objects, the == operator compares the values of the strings, even though strings are objects.

For a user-defined class such as Student that has overridden the Equals method, there is also a way to override the == operator, but the means of doing so is beyond the scope of this book to address.

13.3.2. ToString Method

The most commonly used (and most commonly overridden) Object class method is ToString. It's used to return a string representation of the object on which the method is called, and has the following header:

public virtual string ToString()

The Object class implementation of ToString simply returns the fully qualified name of the type of the object on which it's called. For example, if the Student class belongs in the SRS namespace and we were to call the ToString method on a Student object as it's inherited from the Object class:

Student s = new Student();
s.Name = "Dianne Bolden";
s.Id = "999999";
Console.WriteLine(s.ToString());

the following output would result:

SRS.Student

However, simply printing out the name of the class that an object belongs to isn't very informative. Fortunately, as was the case with the Equals method, the Object class version of the ToString method is declared to be virtual, which enables us to override its behavior for a derived class.

13.3.2.1. Overriding the ToString Method

In the preceding example, we'd prefer that the ToString method for a Student return a more informative result, such as perhaps the label "Student:" followed by a given student's name and student ID number, formatted as shown here:

Student:  Dianne Bolden [999999]

To achieve this result, we'd simply need to override the ToString method in the Student class as follows:

public class Student {
  // Fields.
  private string name;
  private string id;
  // Other details omitted...

  public override string ToString() {
							return "Student:  " + Name + " [" + Id + "]";
							}

Now, the snippet shown earlier:

Student s = new Student();
s.Name = "Dianne Bolden";
s.Id = "999999";
Console.WriteLine(s.ToString());

would output the desired result when executed by virtue of our overridden ToString method:

Student:  Dianne Bolden [999999]

13.3.2.2. "Behind the Scenes" Use of ToString

As it turns out, the ToString method is often called behind the scenes—for example, by the Console.WriteLine method. The Console.WriteLine method, which we normally think of as accepting string arguments, is overloaded so that an arbitrary object reference can be passed as an argument to the method. It's therefore perfectly acceptable to write code as follows:

Student s = new Student();
s.Name = "Cheryl Richter";
s.Id = "123456";
Console.WriteLine(s);

When an arbitrary object reference is passed to the WriteLine method, the WriteLine method automatically calls the object's ToString method to obtain its class-specific string representation, which is then printed to the console. Given the way in which we'd overridden the ToString method for the Student class earlier, the result of running the preceding code snippet would produce the following output:

Student:  Cheryl Richter [123456]

Many of the predefined classes in the .NET Framework libraries have overridden the ToString method. What's more, it's a good idea to get into the habit of overriding the ToString method for all user-defined classes, to ensure that whenever ToString is called behind the scenes on an instance of such a class, a meaningful result is returned.

..................Content has been hidden....................

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