views:

213

answers:

6

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?

+2  A: 

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.

R. Bemrose
Ah. Very interesting. Calling GetType on the instance that was passed in to ClockIn gives me the original type. So I can definitely see that its original type is preserved, even after down-casting.
Nick R.
+7  A: 

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() {
        ....
    }
}
Greg Hewgill
What you're asking about isn't in metadata per se, it's about Polymorphism and virtual tables. You can read the gist of it here:http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Companion/cxx_crib/virtual_functions.html
CubanX
That is actually an excellent refactoring, Greg, although I'm a little more interested in the "what's going on behind the scenes" aspect of my example. Where you say it changes your view is interesting to me.
Nick R.
ClockIn also needs to be virtual ;)
Courtney de Lautour
A: 

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.

Andrew Bezzub
+3  A: 

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.

Jason Young
I'm getting a pretty clear picture now, I think. The object, no matter what it's cast to, always retains its original Type info. The variable that the object is stored in has knowledge to connect to the methods and properties only of the type it has been cast to. Because the Type info is always intact, I can up-cast and the hidden class member will come back into view.
Nick R.
@Jason: Depending upon the type of cast, casting may simply affect how the compiler things about the reference to the object, or it may create a new object whose content was somehow derived from the original object. There are times this behavior may be useful, but it can be very confusing if applied to mutable objects.
supercat
@supercat: Are you referring to casting of value types (i.e. boxing and unboxing)?
Jason Young
@Jason: It's possible for a class or structure to define narrowing or widening typecast operators to or from any other class or structure, provided that the cast is not from a derived type to one of its base types. For example, one could define a HandyStringBuilder class which included widening conversions to and from string; an HandyStringBuilder could be passed to routines expecting strings and vice versa, but since converting a HandyStringBuilder to a string and back would yield a different HandyStringBuilder object, such a design would probably be unwise.
supercat
@Jason: There are times such implicit widening typecasts can work nicely, if the objects are immutable, and if objects resulting from a particular typecast will be indistinguishable; but such casts should be used with caution. An example of a case which might work would be a "FastHash(of T)" which holds an object of type T along with its HashCode (computed once). If T is immutable, and "foo" and "bar" are semantically-equal objects of type T, casting "foo" and "bar" to FastHash(of T) would yield semantically-equal objects of FastHash(of T), and casting back would yield the original objects.
supercat
A: 

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.

supercat
A: 

What you are doing is to create an Implicitly Typed Local Variable, it means that the appropriate type assigned to bob variable (var bob = new Manager()), will be determine (inferred) by the compiler at compilation time. It is not recommended to use implicitly typed variables when is has no sense. The primary usage of ITLV is for LINQ, because the result of a LINQ expression (that uses the new operator) is a type which is determined at compiled time and therefore can be assigned only to ITLV.

Polymorphism is the ability of the compiler to determine at runtime the appropriate derived class of an object that is pointed through a pointer to its base class. In C# the objects metadata contains information about its type and about its members, using reflection you can read an object metadata. Excessive usage of casting and typeof(MyClass) is suggestion that a bad design is being made.

ArceBrito