Casting classes

  • Thread starter Thread starter J L
  • Start date Start date
J

J L

I want to be sure I understand the rules when casting classes and
sub-classes.

If I have a class Person and a sub-class Employee that inherits
person, then

This is not allowed. Why?
dim person1 as new person
dim emp1 as employee
emp1 = person1

But this is. Why?
dim person1 as person
dim emp1 as new employee
person1 = emp1

TIA
John
 
If Employee derives (inherits, subclasses, etc.) from Person, then Employee
must have all of the "exposed" members of Person, and possibly some more
specific information that has been added to its definition. So casting
Employee to Person is ok because, from the compilers standpoint, all the
"exposed" members are accessible through the base Person class. However, it
could not be guaranteed that the opposite is true since Employee may have
added information to its class definition that does not exist in the base
Person class. In other words, Employee has *everything* that Person has but
Person may or may not have everything that Employee has. Clear as mud,
right? :)
 
Aha..I think I get it. It is a bit of a mind twister...but now I see
that when I say emp1=person1 I am really changing the pointer of the
emp1 object from an instance of employee to an instance of person but
the employee sub-class has method etc that are not in the instance of
person so any other references of emp1 to those sub-class methods will
not be valid...and VOILA...you cant do it!! Huh????

And add to all of that, I am trying to understand overriding,
overloading, overloading overriden and god forbid shadowing all on an
inherited interface with friendly events but protected
properties...LOL Long live OOP or should that be POOP...

But it is fun, isn't it?

John
 
Ah, it's not that bad. You'll get the hang of it. Once you take hold of the
concepts it actually allows you to think more "real world" about problems.
Things in the real world are built from smaller components, aka objects. So
OOP allows you to think of software the same way as you think of other
products that you buy or build. Everything is based on something and so
objects get put together to form larger, more defined, objects and bam you
got yourself an application. It's almost like playing with lego.

The case that you were dealing with is a common one to see for, arguably,
the most complicated OOP concept - polymorphism.
http://msdn.microsoft.com/library/d.../html/vbconinheritancepolymorphismallthat.asp
 
Think of it this way.

An employee is definitely a person, so the compiler knows that

person1 = emp1

will always be valid, no matter what. However, not all people are
employees. Any given person _might_ be an employee, but then again they
might not. So, something like

emp1 = person1

may or may not be valid, depending upon what the reference in person1
points to. This is why the compiler requires an explicit cast:

emp1 = (employee)person1;

This is your promise to the compiler that yes, you know what you're
doing, and person1 points to a person that was created like this:

person1 = new employee

or assigned from an employee:

person1 = anEmployee

These two lines of code are perfectly legal because, again, employees
are always people.

In all of this, it's important to keep a clear distinction between
compile-time and run-time. At compile time, the compiler cannot tell
what kind of object "person1" is referring to. All it knows is that it
must be a "person" or any of the classes that inherit from "person".
So, when you say

emp1 = (employee)person1;

the compiler builds in code to check _for sure_ at run-time that the
thing that person1 refers to really is an employee (or some class
derived from employee). If it isn't, then the CLR generates an
InvalidCastException exception.

However, as I said, this is at run-time. At run-time, your program is
running with real, created objects. Actual "person" and "employee"
objects with memory assigned to them and all. Once you create an object
of a particular type, _it is that type for its whole existence_. You
_cannot_ change what type of thing it is.

What this means is that when you say:

person1 = emp1;

you are _not_ "changing the employee into a person" as many people seem
to think. You are just changing a variable of type "person" to point to
a specific kind of person: a particular employee. The employee object
itself never changes what it is.

This is where overloading comes in: if you say this:

person1 = emp1;
person1.SayHello();

does _not necessarily_ invoke the SayHello() method in the "person"
class. Because person1 points to an employee object (and it never
changes what it is at run-time), this call will invoke whatever the
employee class says the SayHello() method should do. If "employee"
doesn't define a SayHello, then maybe it falls back to the person
(parent) class, but if it does define a SayHello of its own (using
"override") then _that_ is the method that will be invoked.

Just because there is a "person" variable pointing to the object does
not change what it is or what methods are called on it. (There is an
exception to this: methods declared as "new", but don't worry about it
for now. :)
 
Hi Bruce,
Thanks for the detailed explaination. I thought I was clear but now I
am a bit confused again (don't worry, that is not your fault...seems
to be a mental characteristic of mine since I began studying
..Net...welll not really but..).

I tried the following test:

Dim person1 As New Person
' Employee inherits from Person
Dim emp1 As New Employee

' Birthdate is property of Person
person1.Birthdate = CDate("01/01/2000"(
emp1.Birthdate = CDate("01/01/2001")

' Age is method in both classes...Person computes the correct age
' Employee overrides and computes a negative age
Messagebox.Show(person1.Age.ToString)
person1 = emp1
MessageBox.Show(person1.Age.ToString)

The interesting thing is that Employee overrides Age so that it
computes the negative age. But when I run this, the output shows that
while the first output was the original person1 age and the second one
was the employee age in both cases the Person method was used (both
ages were positive and this can only happen when the Person's Age
method is used). So somehow this assignment caused the person1 object
to pickup the emp1 property (Birthdate) but used the Person Age
method.

Your comment was:
person1 = emp1;
person1.SayHello();

does _not necessarily_ invoke the SayHello() method in the "person"
class. Because person1 points to an employee object (and it never
changes what it is at run-time), this call will invoke whatever the
employee class says the SayHello() method should do.

So somehow, when you take an object of Person and assign it to an
object of Employee, it picks up the "Person-ish" part of employee but
ignores any overriden methods.

Is that correct?

All of this says one thing to me...KISS...dont mix object types...dont
try to be cute...define and use the types in a straightforward way or
it may cuase some very hard to decipher behavior.

I think OOP tries to be all things to all people. And while there are
geniuses who can keep it all straight, the blue collar programmers,
such as myself, need to focus on the basics.

John
 
Hi Bruce,
It gets even more strange...

If I do not use the New in the person1 declaration and then say

person1 = emp1

and then

emp1.Dispose()
person1.Dispose()

The Dispose method gets called twice (as expected) but the constructor
was not called. So if I am keeping track of instances with a counter
in the constructor/dispose methods...it is completely wrong.

So, my conclusion, is dont even think of doing this. And I can not
remember why I even tried it to begin with :>)

John
 
The interesting thing is that Employee overrides Age so that it
computes the negative age. But when I run this, the output shows that
while the first output was the original person1 age and the second one
was the employee age in both cases the Person method was used (both
ages were positive and this can only happen when the Person's Age
method is used). So somehow this assignment caused the person1 object
to pickup the emp1 property (Birthdate) but used the Person Age
method.

How are you declaring Age in the Employee class? You should be using
"Overrides" (and it should be declared "Overridable" in the Person
class). I'm not a VB.NET programmer (I program in C#), but the VB.NET
language spec says that if you don't mention any keyword on the Age
method in the Employee class, then the default is Shadows, which has
the behaviour you saw. For now, until you understand things better, you
should always use Overridable and Overrides.

(I have no idea why the VB.NET designers decided to make the least
useful case (Shadows) the default. Odd design decisions like this one
are why I don't program in VB.NET. :)
(There is an exception to this: methods declared as "new", but don't
worry about it for now. :)

Sorry about that... I was using C# terminology there with that "new".
The VB.NET equivalent is Shadows, which you get by default.
So somehow, when you take an object of Person and assign it to an
object of Employee, it picks up the "Person-ish" part of employee but
ignores any overriden methods.

No. Only if you forget to use say "Overrides when declaring your
methods / properties in the child class.
I think OOP tries to be all things to all people. And while there are
geniuses who can keep it all straight, the blue collar programmers,
such as myself, need to focus on the basics.

It's not that bad. Really... it's not. Use "Overrides". Avoid "Shadows"
for now. I think you'll see that the behaviour is quite predictable.
 
Hi Bruce,
You are exactly right. I did not use overrides so it did pick up the
base class implementation.

Actually I am enjoying learning OOP and will use it. And I will take
your advice and keep away from of the wierder stuff, like Shadowing.
In the book I am reading I put a note in the margin to never use
Shadowing...seems to be just an opportunity for confusion.

Thanks again for your time and expertise. That is the value of these
news groups...having mentors such as you and the other fantastic
contributors who take the time to answer questions and give guidance.

John
 
Back
Top