0

A few days ago in an interview, i was asked to explain following cases:

public class Parent
{

}
public class Child:Parent
{

}

Case1:

Parent p = new Child();
    var c = (Child)p;

Case2:

    Parent p = new Parent();
    var c = (Child)p;

I said case1 is valid and case2 is invalid , but he asked:

Why case2 is invalid while both p variable in case1 and case2 are Type of Parent class?

In case2 we get runtime exception by following message:

Unable to cast object of type 'Parent' to type 'Child

While in case1 p variable was also type of Parent class.

He wanted me to explain it from point of stack and heap memory.

Have you any idea?

Update:

The very spot that interviewer misguided me was exactly where he said in both cases p is type of 'parent' class, based on the answers, just static types of both variables are 'parent', but in run-time they have different types and it's run-time type that really matters.

Abolfazl
  • 1,592
  • 11
  • 32
  • 1
    Because in case 2 `p` isn't a `Child` type and therefor cannot be cast into one. In case 1 this isn't a problem because, although stored in a `Parent` variable, `p` actually **IS** a `Child` instance. You can compare this by creating an `new object()` instance. Anything is an object in c# but an object is not everything. – Jack T. Spades Jun 13 '21 at 16:00
  • Does this answer your question? [Why downcasting is not allowed in C#?](https://stackoverflow.com/questions/58728416/why-downcasting-is-not-allowed-in-c) and [How to create a new object instance of class being the same type of another instance by only knowing it as base class type?](https://stackoverflow.com/questions/65913153/how-to-create-a-new-object-instance-of-class-being-the-same-type-of-another-inst/65913434#65913434) –  Jun 13 '21 at 16:01
  • Does this answer your question? [Upcasting and its effect on the heap](https://stackoverflow.com/questions/47390220/upcasting-and-its-effect-on-the-heap) – Peter Dongan Jun 13 '21 at 16:03
  • OK, then will it be correct to say 'p' in case1 is type of Child but it's type is defined on runtime not compile time? @Jack T. Spades – Abolfazl Jun 13 '21 at 16:15
  • @Abolfazl No, that's not correct. it is "defined" at compile-time, and the object instance being of the expected type is created at runtime. It is what we call *strong typing*: [Why is C# statically typed?](https://stackoverflow.com/questions/859186/why-is-c-sharp-statically-typed) –  Jun 13 '21 at 16:17
  • but in case1 after it gets 'p' type by 'p.GetType()' it says 'p' is type of 'Child', but before running program it is known as 'Parent' type. – Abolfazl Jun 13 '21 at 16:23
  • @Abolfazl It's [polymorphism](https://stackoverflow.com/questions/1031273/#58197730): you have a Child known as Parent, but it's a Child. Legal in [OOP](https://www.c-sharpcorner.com/UploadFile/mkagrahari/introduction-to-object-oriented-programming-concepts-in-C-Sharp) | [Classes](https://www.c-sharpcorner.com/UploadFile/84c85b/object-oriented-programming-using-C-Sharp-net) | [Abstraction](https://stackoverflow.com/questions/58765776/#58766026) | [Encapsulation](https://stackoverflow.com/questions/58257849/#58258056) | [Interface & Class](https://stackoverflow.com/questions/10914802/#58174007) –  Jun 13 '21 at 16:45
  • 1
    I wonder what the interviewer was getting at by asking about stack and heap storage; the question as you've described it has very little to do with either. – Eric Lippert Jun 14 '21 at 15:29

1 Answers1

3

You must make a distinction between the static type of a variable (the type known at compile time) and the run time type of an object reference assigned to a variable.

The static type of p is Parent in both cases because it is declared as Parent p. No matter what you assign it. You may even assign it null.

The run time type of the value of p after the assignment is Child in the first case and Parent in the second case. It would be undetermined if p was null.

It is okay to assign a Child to a Parent, because the Child class derives from Parent. A Child is therefore a Parent (in OO terms).

However, a Parent is not a Child, because it does not derive from it. Therefore, in the second case, you cannot cast p to Child. In the first case the cast is valid, because you cast the object to its actual run time type. It tells the compiler, I know that this object assigned to a Parent variable is a Child, so, please, treat it as a Child. This does not involve any conversion; however, a runtime check will be performed, possibly throwing an exception, if the cast was not allowed.

You were asked to explain it from point of view of stack and heap memory. I'm sorry to say, but this has absolutely nothing to do with stack or heap memory. It has to do with inheritance rules and assignment compatibility.

Of course, here we are dealing with reference types (classes). This allows us to derive Child from Parent. This would not be possible with value types (structs). The point is reference type versus value type, not heap versus stack, which is an implementation detail. And btw., a value type (struct) field in a class will also be stored on the heap.

See also: The Stack Is An Implementation Detail, Part One and Two (Eric Lippert's blog)


You did not ask for it but casting to a derived type is mostly preceded by a type test, because you usually do not know the run time type in advance. Let us say that the Child class adds a new method DriveParentsToDespair() (only children can do this). Then you could use Type testing with pattern matching to test the type and assign it to a new variable at the same time:

if (p is Child child) {
    child.DriveParentsToDespair();
}

This avoids an exception in case p has a run time type of Parent.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • is it kind of 'dynamic polymorphism'? – Abolfazl Jun 13 '21 at 16:57
  • 1
    @Abolfazl It ***IS*** polymorphism (dynamic) as indicated by my comments: *manipulating a child only knowing an ancestor* (using upcast). –  Jun 13 '21 at 17:03
  • 1
    Static and compile-time type was a good point. – Abolfazl Jun 13 '21 at 17:21
  • 2
    Polymorphism is dynamic and comes into play when you are calling a virtual method. E.g. If `Parent` had a virtual Print method printing "I am a parent" to the console and the `Child` class did override this method to have it print "I am a child", then the outcome of calling `p.Print();` depends on the run time type of `p`. – Olivier Jacot-Descombes Jun 13 '21 at 17:30
  • Yeah you are right, polymorphism is a vast concept in 'oop'. – Abolfazl Jun 13 '21 at 17:48
  • 1
    the exact point as you mentioned in your answer was the we need to differentiate between static type and runtime type. – Abolfazl Jun 13 '21 at 17:50
  • Your answer was a great help.@OlivierJacot-Descombes – Abolfazl Jun 13 '21 at 17:53