9

For the following classes:

public class Parent {
//Parent members
}

public class ChildA : Parent {
//ChildA members
}

public class ChildB : Parent {
//ChildB members
}

If I upcast ChildA or ChildB instance to a Parent instance, then I can't accesses their members, but their members are still there, because if I downcast and try to access their members again I will find that they still have their data.

I think this means that the Parent Instance keep allocating memory for the Child classes.

So does this mean when I instantiate a Parent Class that its allocating memory for the child classes members, or is that just happening when I cast?

And is it possible for a parent to allocate memory for more than one child if we go backward and forward with casting?

Secespitus
  • 710
  • 2
  • 14
  • 22
Honey
  • 169
  • 1
  • 8
  • Differentiate between object and variable. When you create an instance, you're creating an object. When you cast, you're casting the variable, not the object. – Rotem Nov 20 '17 at 10:58

4 Answers4

14

In the case you describe above, casting does not affect the memory that is allocated when casting from base to sub class and vice versa.

If you instantiate a Parent you will have a Parent object in memory. If you cast that to either of the child classes it will fail with an InvalidCastException.

If you instantiate either child you will have a child object in memory. You can cast this to the Parent and then back again. The memory allocation does not change in either case.

Additionally, if you instantiate a ChildA, cast to Parent and then attempt to cast to ChildB, you will get an InvalidCastException

Tim Rutter
  • 4,549
  • 3
  • 23
  • 47
  • @Honey - For a reference type (`class`) A variable merely holds a pointer to the allocated memory. Casting forth and back doesn't change the pointer nor does it change the allocated memory. It merely changes how the value of that pointer is interpreted. For a value type (`struct`, and also basic types like `int`, `float`, `byte`, etc) the value is stored in the variable itself. Since value types cannot be inherited, most casts (like from `int` to `float`) will simply perform some conversion and store the results in the target variable. – Vilx- Nov 20 '17 at 11:09
  • 2
    Casting does not effect the memory that is allocated in any circumstance.-> true for _this type_ of casting (subclass -> parent class, class -> implemented interface etc), might be worth noting that this is only true 'in any circumstances except where an implicit / explicit cast operator exists, is used, and allocates memory' – tolanj Nov 20 '17 at 11:09
  • @Honey - Most of the time there's no extra memory allocation, except if you do something fancy in your cast operator. Actually, that's true for `class`es as well - if you have an explicit/implicit cast operator defined, all bets are off. Lastly, a value type can be cast to `object` (which is a reference type) and that involves "boxing" - this actually allocates another copy of the object on the heap and stores the pointer to that copy in the variable. Casting back and forth between reference and value types then will involve memory allocations. – Vilx- Nov 20 '17 at 11:10
  • Thanks for comments, I've changed my answer, don't want to get bogged down in the detail however so not mentioning explicit cast operators and boxing as they will just confuse the answer. – Tim Rutter Nov 20 '17 at 11:32
6

"Normal" Upcasting and Downcasting of Reference Types

For reference types, casting variables doesn't change the type of the object already allocated on the heap, it just affects the type of the variable which references the object.

So no, there isn't any additional heap overhead with casting reference types (i.e. object instances from classes) provided that there are no custom conversion operators involved (See below, tolanj's comment).

Consider the following class hierarchy:

public class Fruit
{
    public Color Colour {get; set;}
    public bool Edible {get; set;}
}

public class Apple : Fruit
{
    public Apple { Color = Green; Edible = true; KeepsDoctorAtBay = true;}
    public bool KeepsDoctorAtBay{get; set;}
}

Which, when used with both upcasting and downcasting:

Example of variables pointing to same heap object

There is only ever one allocation on the heap, which is the initial var foo = new Apple().

After the various variable assignments, all three variables, foo, bar and baz point to the same object (an Apple instance on the heap).

Upcasting (Fruit bar = foo) will simply restrict the variable's available access to only Fruit methods and properties, and if the (Apple)bar downcast is successful all methods, properties and events of the downcast type will be available to the variable. If the downcast fails, an InvalidCastException will be thrown, as the type system will check the type of the heap object's compatability with the variable's type at run time.

Conversion Operators

As per tolanj's comment, all bets about the heap are off if an explicit conversion operator replaces the default casting of reference types.

For instance, if we add an unrelated class:

public class WaxApple // Not inherited from Fruit or Apple
{
    public static explicit operator Apple(WaxApple wax)
    {
        return new Apple
        {
            Edible = false,
            Colour = Color.Green,
            KeepsDoctorAtBay = false
        };
    }
}

As you can imagine, WaxApple's explicit operator Apple can do whatever it likes, including allocate new objects on the heap.

var wax = new WaxApple();
var fakeApple = (Apple)wax;
// Explicit cast operator called, new heap allocation as per the conversion code. 
StuartLC
  • 104,537
  • 17
  • 209
  • 285
4

A (down-)cast is nothing but a view onto an instance of a class by the "eyes of the parent class". Thus you´re neither losing nor adding any information nor memory by casting, you simply reference the same memory allready allocated for the original instance. This is the reason why you can still access (e.g. by reflection) the members of ChildA in the variable of type Parent. The information still exists, it is simply not visible.

So instead of having two memory-allocations you have two memory-references.

However be aware that this does not apply if you provide your own cast, e.g. from ChildA to ChildB. Doing so will typically look more or less similar to this:

public static explicit operator ChildA(ChildB b)
{
    var a = new ChildA((Parent)b);
    /* set further properties defined in ChildA but not in ChildB*/
}

Here you have two completely different instances, one of type ChildA and one of type ChildB which both consume their own memory.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • that explain it , I had different idea to how casting work , I was thinking that your example was possible thanks for the explain – Honey Nov 20 '17 at 12:17
1

I think this means that the Parent Instance keep allocating memory for the Child classes .

No, because Parent class does not know about it's children.

var a = new ClassA();

.NET allocates memory for all members of ClassA.

var b = (Parent)a;

.NET does not do anything with memory. a and b point to the same memory block (allocated for ClassA).

Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
Backs
  • 24,430
  • 5
  • 58
  • 85
  • thanks for the answer Backs , I was thinking like that because a parent var is holding a child and still can cast it down and accesses its members , but know I get it thanks for the explain – Honey Nov 20 '17 at 11:05