downcasting... can't do it but...

  • Thread starter Thread starter Rein Petersen
  • Start date Start date
R

Rein Petersen

Hi Folks,

I've accepted that one cannot downcast between types, however I can't find a
way to achieve

//...

class Employee
{
public string name;
}

class ContractEmployee : Employee
{
public string contractID;
}

class SomeOtherKindofEmployee : Employee
{
public string relevantAttribute;
}

class SampleProgram
{

public static void Main()
{
//we need to create the employee object and work with it
Employee e = new Employee();
e.name = "Fred";
//later, we need to specify the Employee as a ContractEmployee
// but the operation will fail because it is a down-level cast.
ContractEmployee c = (ContractEmployee) e;
c.contractID = "1234";
}
}

//...

How else would I create the 'ContractEmployee' object without having to
explicity copy the value's from all of the Employee object's properties to
the newly created ContractEmployee object's properties? I mean, it seems to
me that the 'ContractEmployee' object is just an extension of the 'Employee'
object so why cant I just add properties to an object by changing it's type
to a downlevel (child) type?

Rein
 
In order to downcast from Employee to ContractEmployee you
must be working with an instance of ContractEmployee. In
your code snippet you instantiated an "Employee" --> and
that is what you've got. This code will work on the first
call to "SomeMethod()" but it will fail on the second:

ContractEmployee ce = new ContractEmployee();
Employee e = new Employee();

SomeMethod(ce);
SomeMethod(e);


SomeMethod(Employee e)
{
ContractEmployee ce = (ContractEmployee) e;
}


In order to do what you want you need to add a constructor
form or a "Convert" method to ContractEmployee and
SomeOtherKindOfEmployee that takes an Employee as an
argument and then manually converts to the new type...

--Richard
 
Thanks Richard,

This is what I had suspected but had hoped I might avoid. It occurs to me
that a downlevel cast *should* be allowed in cases of inheritance - just as
a sensible convenience. So " ContractEmployee ce = (ContractEmployee) e; "
would simply provide ce with it's inherited attributes - similar to boxing.
That's the whole idea of inheritance isn't it? To normalize the data by
generalizing...

To put it another way, if through inheritance, we can say a ContractEmployee
*is* an Employee, then why can't I perform this downlevel cast?

There must be a good reason but I'm not familiar with it...

Rein
 
Rein... The reason is that you are not creating a new object of class
contractemployee. Instead, you are simply creating a new reference of
type contractemployee. A subtle, but important point.

http://www.geocities.com/jeff_louie/OOP/oop6.htm

Chapter 6 "Objects Have Class, References Have Type" 

Well, you have arrived. If you have survived to this point, you are
beginning to realize both the power and complexity of programming
using objects and inheritance. Much of the confusion about references,
types, classes and objects is simplified by the following statement
"Objects have class, references have type." The more technically correct
version may be "Objects have class, reference variables have type." As
long as it is understood that reference variables "contain" references
to
objects, the former statement is a superior in its simplicity

Regards,
Jeff
To put it another way, if through inheritance, we can say a
ContractEmployee *is* an Employee, then why can't I perform this
downlevel cast?<
 
Rein Petersen said:
This is what I had suspected but had hoped I might avoid. It occurs to me
that a downlevel cast *should* be allowed in cases of inheritance - just as
a sensible convenience. So " ContractEmployee ce = (ContractEmployee) e; "
would simply provide ce with it's inherited attributes - similar to boxing.
That's the whole idea of inheritance isn't it? To normalize the data by
generalizing...

To put it another way, if through inheritance, we can say a ContractEmployee
*is* an Employee, then why can't I perform this downlevel cast?

A ContractEmployee *is* an Employee, but not every Employee is a
ContractEmployee.
There must be a good reason but I'm not familiar with it...

Safety. You shouldn't use objects in a way that they weren't
constructed for. For instance, what would you expect the following code
to do?

object o = new object();
FileStream fs = (FileStream) o;
fs.Read();

That's just as valid as your trying to cast an Employee instance to a
ContractEmployee instance - in the latter case, the ContractEmployee
couldn't have a valid contract id, for a start.
 
Thanks Richard,

This is what I had suspected but had hoped I might avoid. It occurs to me
that a downlevel cast *should* be allowed in cases of inheritance - just as
a sensible convenience. So " ContractEmployee ce = (ContractEmployee) e; "
would simply provide ce with it's inherited attributes - similar to boxing.
That's the whole idea of inheritance isn't it? To normalize the data by
generalizing...

To put it another way, if through inheritance, we can say a ContractEmployee
*is* an Employee, then why can't I perform this downlevel cast?

There must be a good reason but I'm not familiar with it...
<snip>
The reason is because you never created a ContractEmployee in the
first place. You created a vanilla Employee. If the compiler
permitted such a downcast, then you could refer to fields/methods/
properties via such a ContractEmployee reference that are not
present in the originally created object, because you just
created a simple Employee.

Oz
 
Consider this:

You instanced an employee object. That's what is actually in memory, and
memory was allocated just for that object.

Now, if this were allowed:
contractemployee ce = (contractemployee)e;
Where would the contractID property go? Memory was never allocated for it
because you actually asked for an employee.

Now, you can do the exact opposite of this
(this code is completely legal --unless I goofed up the syntax <g>):

Employee e = new ContractEmployee();

e.Name = "Fred";
ContractEmployee ce = (ContractEmployee)e; // this works because e really is
a ContractEmployee
ce.ContractId = "1234";

Now, you can allow some indirection by having a method that returns the
right kind of employee:

Employee e = EmployeeFactory.GetEmployee(); // Get employee figures out what
kind of employee to instance, in this case a ContractEmployee
if(e is ContractEmployee)
{
ContractEmployee ce = (ContractEmployee)e;
// etc
}
else if(e is SomeOtherKind)
{
}

(however, if you find yourself with a lot of code like this, it could mean
that there is a design problem).

-- or --
You could just use
ContractEmployee ce = (ContractEmployee)e; // exception will be thrown if e
is not a ContractEmployee
-- or--
you could use
ContractEmployee ce = (e as ContractEmployee); // if e is not really a
ContractEmployee, ce == null
 
Back
Top