13.13. Object Identities

In an earlier example, GraduateStudent and UndergraduateStudent objects were stored in a List collection that had been declared to hold Student objects. An element extracted from the List will be returned as a Student object. The extracted object hasn't "forgotten" what class to which it belongs. An object always retains its class identity; it's simply that we can refer to an object with various different reference variables, which might be declared to be of different types, and it's this phenomenon that affects which messages the compiler believes that an object is capable of responding to. To help illustrate this point, let's use an example.

13.13.1. A Derived Class Object Is a Base Class Object, Too

If we instantiate a Professor object, which is a type of Person, and therefore, as we saw earlier, is also a type of Object, we can think of the Professor object as being allocated in memory, as demonstrated in Figure 13-4.

Figure 13.4. A Professor object is simultaneously a Person object and an Object object, all rolled into one!

We can then create reference variables of varying types (any of the types in this object's derivation chain) to store references to this Object/Person/Professor; each reference refers to a different "rendition" of the object:

// Create a Professor object, and maintain three references to it
// of varying types.

Professor pr = new Professor();
Person p = pr;
object obj = pr;

If we refer to this Professor object by its object reference obj, as far as the compiler is concerned, the only aspects of the object that exist are its Object "core": the "Person-ness" and "Professor-ness" of the object is in question (see Figure 13-5).

Figure 13.5. The compiler only knows about this Professor object's "Object-ness."

So, the compiler will reject any attempts to access the Professor- or Person-defined members of obj:

// The compiler would reject this attempt to invoke the
// AddAdvisee() method, declared for the Professor class, on this
// object, because even though WE know from the code above that it
// is really a Professor whose handle is
// stored as an object, the compiler cannot be certain of this.
obj.AddAdvisee();  // compiler error

// However, we can invoke any of the methods that this Professor
// inherited from the Object class:
obj.ToString();

If we instead refer to the Professor object by its Person reference p, as far as the compiler is concerned, the only aspects of the object that exist are its Object "core" and its Person "extensions." The "Professor-ness" of the object is in question (see Figure 13-6):

// The compiler would again reject this attempt to invoke the
// AddAdvisee() method, declared for the Professor class, on this
// object, because even though WE know from the code above that it
// is really a Professor whose reference is stored as a Person at
// run time, the compiler cannot be certain of this at COMPILE
// time.
p.AddAdvisee();  // compiler error

// However, we can invoke any of the methods that this Professor
// inherited from EITHER the Object class...
p.ToString();

//...OR the Person class:
p.Name;

Figure 13.6. The compiler now recognizes this Professor object's "Object-ness" and "Person-ness."

Only if we refer to the object by its Professor reference, pr, will the compiler allow us to access Professor-specific members of the object.

13.13.2. Determining the Class That an Object Belongs To

There are several ways to ask an object what class it belongs to at run time; we can take advantage of the following:

  • The GetType method defined by the Object class

  • The typeof operator

  • The is keyword

13.13.2.1. The GetType Method

As mentioned earlier, by virtue of being derived from the Object class, every object inherits a method with the header:

public Type GetType()

When invoked on an object, this method returns an object of type Type, representing the type of the object on which the GetType method has been invoked. For example, if the Person class is defined to belong to the SRS namespace, then the following invocation of GetType would yield a Type object representing the class SRS.Person:

Person p = new Person();
Type t = p.GetType();

The Type class, in turn, defines a string property named FullName that can be used to access the fully qualified name of the type:

Person p = new Person();
Type t = p.GetType();
String s = t.FullName; // s now equals "SRS.Person".

Chaining the method call and property invocation together, we can ask any object reference to identify which class the object it refers to belongs to, as follows:

reference.GetType().FullName;

For example:

using System;
using System.Collections;

public class CastingExample
{
  static void Main() {
    Student s = new Student();
    Professor p = new Professor();

    ArrayList list = new ArrayList();
    list.Add(s);
    list.Add(p);

    for (int i = 0; i < list.Count; i++) {
      // Note that we are not casting the objects here!
							// We're pulling them out as generic objects.
							object o = list[i];
							Console.WriteLine(o.GetType().FullName);
    }
  }
}

This program produces as output (assuming that neither Student nor Professor belongs to a named namespace):

Student
Professor

This demonstrates that the objects themselves really do remember their roots!

13.13.2.2. The typeof Operator

Another way to test whether a given object reference belongs to a particular class is via the typeof operator. This operator takes as its argument the name of a type (note: the type name is not quoted) and returns the corresponding Type object. Here is a simple code snippet to illustrate how the typeof operator can be used to see whether a reference is of a certain type. (The == operator works in this case because there is only one System.Type object for each type in the C# language, whether user-defined or predefined.)

Student s = new Student();

// Determine if the type of the s reference variable is
// is equal to the Student type.
if (s.GetType() == typeof(Student)) {
  Console.WriteLine("s is a Student");
}

The preceding code would produce the following as output:

s is a Student

Note that if we want to refer to the name of a class that belongs to an explicitly named namespace, and we haven't included the appropriate using statement with our code, we must fully qualify the class name when using typeof:

// Assume that Bar is a class in the Foo namespace.
if (x.GetType() == typeOf(Foo.Bar) {...}

NOTE

Although the use of parentheses to surround the name of a class—typeof(Student)—makes it look as if typeof is a method, it's indeed an operator.

13.13.2.3. The is Operator

A third way to determine whether an object is of a certain type is by using the is operator, which typically generates cleaner code than the typeof operator. For instance, here is the previous example using the is operator:

Student s = new Student();

// Determine if the type of the s reference variable is
// is equal to the Student type.
if (s is Student) {
  Console.WriteLine("s is a Student");
}

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

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