开发者

When an object is cast to a base class, how does it remember what it really is?

This is a beginner's question, but I am interested in learning what's going on here. My question is, what goes on behind the scenes when you down-cast an object? Does it maintain some sort of metadata about what it originally was? Here's what I mean:

Suppose I have a method called "ClockIn" that accepts a parameter of type "Employee":

public static void ClockIn开发者_运维百科(Employee employee)
{
   var manager = employee as Manager;
   if (manager != null)
   {
      manager.OpenSafe();
   }
}

So, assume that Manager is a subclass of the Employee type and that it has the "OpenSafe" method:

public class Manager : Employee 
{
   public void OpenSafe()
   { 
      ... 
   }
}

The "ClockIn" method, if it finds that a Manager has been passed in, calls the OpenSafe method. Such as:

var bob = new Manager();
ClockIn(bob);

Here, I've passed in an instance of type Manager into a method that accepts the base class Employee. I need to cast the instance inside the ClockIn method to Manager before I can call OpenSafe.

The question is, is there some metadata that remembers that "bob" is a Manager, even though I've passed him in as an Employee? How does the code know that he can indeed be cast to a Manager? Is there something going on in the heap?


The first thing to remember is that casting does not change the original object at all. It only changes your view of the object through that particular reference. More than one reference can be pointing to the same object, so changing the object isn't a reasonable thing to do on a cast.

What you might do in your instance is to make ClockIn() a method of the Employee class. Then, when you call

bob.ClockIn();

then bob will know what type he really is, and call the appropriate ClockIn() method for his type. This is called dynamic method dispatch and is not available for static functions as in your example.

For example:

public class Employee {
    public void ClockIn() {
        ....
    }
}

public class Manager: Employee {
    public void ClockIn() {
        // first, do what all Employees do when clocking in
        Employee.ClockIn();
        // Next, do Manager specific actions
        OpenSafe();
    }
    public void OpenSafe() {
        ....
    }
}


Casts do not change the runtime type of an object.

Calling the GetType method on an object will return a Type object representing its runtime type.


Casting does not affect the object - it affects the reference to the object.

All you are doing when you downcast an object is tell the compiler that this object can be referenced by a variable that derives from the type currently referencing the object.

The key point is the class of the object never changes, you are only changing the type of the reference to the object.


Actual type of object is always stored as link to its System.Type. Actually each object .NET have additional System.Type field referencing its actual type.


Extending the above answers, it's helpful to think of the public aspects of a class as being like an electrical panel. Fixed outside-world connection-points attach to the inner workings of the class. If a class inherits another class, objects of the new class get a panel labeled with their new class type, in addition to having a panel of the base class type. Adding an interface adds yet another panel.

If a property or method is declared overridable, the back of the panel "connection" for that method/property will have a detachable plug; otherwise it won't. Suppose a class "Alpha" has an overridable method "foo" and non-overridable function "bar". A derived class "Bravo" overrides "foo" and shadows "bar". Objects of class "Bravo" will have both "Alpha.foo" and "Bravo.foo" wired to Bravo's "foo" function; they'll have "Alpha.Bar" wired to Alpha's "Bar" function, and "Bravo.Bar" wired to Bravo's "Bar" function.

If an object "BravoInstance" of type "Bravo" is used someplace where a "Bravo" is expected, a reference to its "BravoInstance.Bar" will cause the system to look at the object's "Bravo" panel and use its "Bar" connection (wired to Bravo.Bar). If such an instance is given to code that expects an Alpha (perfectly permissible because of inheritance), an attempt to access ThatInstance.Bar will connect to the "Alpha" panel's "Bar" connection (which is in turn wired to Alpha.Bar).

There are times shadowing is useful and appropriate, but one must be very careful when shadowing a method or property of an object which may be passed to code that expects the base type.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜