Open In App

What is Object Slicing and Why it Doesn’t Happen in C#?

Improve
Improve
Like Article
Like
Save
Share
Report

If we have two classes class A and class B, and B is derived from A then when we assign an object of class B to an object of class A then all additional attributes of class B object are sliced off to form the base class object (class A object). In simple words, when extra components of a derived class are sliced or not used and the priority is given to the base class’s object this is termed object slicing. 

Now you’ll be wondering why we have to prioritize the objects when everything is stored in the class, but this is where many coders go wrong; the memory value is stored in the object of a class and never in the class in itself. Hypothetically, if we only have 20 KB of space where we have to add the objects of both Class A and Class B and the object of class A is consuming space of 20 KB then the object of Class B will be sliced automatically. 

Example From the C++ Language:

C++




// C++ program to demonstrate where the
// object slicing can occur
#include <iostream>
using namespace std;
 
// Base class
class A {
public:
    void fun1() { cout << "Function 1" << endl; }
};
 
// Derived Class
class B : public A {
public:
    void fun2() { cout << "Function 2" << endl; }
};
 
// Slicing of object is taking place in this function
void test(A slicedObject) { slicedObject.fun1(); }
 
int main()
{
    B fullObject; // object of class B
    test(fullObject);
    return 0;
}


Output:

Function 1

Explanation: In the above code we made two classes:

  • Class A
  • Class B 

Class A: Contains one function called fun1 which prints to the console the sentence “Function 1”.

Class B: Contains one function called fun2 which prints to the console the sentence “Function 2”, and it inherits from class A so it can use fun1 also.

Now we have the function test which takes one parameter of type A. If we try to pass an object of type B to the function it will succeed but with a cost of what is called object slicing. Now if we try to execute fun2 within the function test it will generate a compile error because it cannot find the function within the object member functions.

Reason: A child class instance can have a size in memory more than or equal to the parent class because a child class contains all the attributes and functions of the parent class and can also have additional attributes or functions.

So, when we try to create an instance of the parent class and assign it an instance of the child class there will be insufficient memory for the parent object to hold all the additional attributes and functions of the child class instance.

So, it slices all the additional attributes and functions off from the child class instance to form the parent class instance which has only attributes and functions found in the parent class.

For more details please refer to the Object Slicing in C++ article.

Same Example in C#:

C#




// C# program to demonstrate where the
// object slicing can occur
using System;
 
// Base class
class A {
    public void fun1() { Console.WriteLine("Function 1"); }
}
 
// Derived class
class B : A {
    public void fun2() { Console.WriteLine("Function 2"); }
}
 
 
class Program {
    public static void Main()
    {
        B fullObject = new B();
        test(fullObject);
    }
    public static void test(A notSlicedObject)
    {
        notSlicedObject.fun1();
    }
}


Output

Function 1

In the above code, we created the same example we did previously but with C# this time.

We will notice that we still can’t use notSlicedObject.fun2(), so what is the difference?

The difference is that notSlicedObject is a reference to an object of type A, so it can only use methods or attributes bounded to that object type.

But we can cast the type of the reference to make it reference an object of type B instead of type A and that way we can access attributes and methods bound to type B.

C#




// C# program to demonstrate where could the
// object slicing can occur
using System;
 
class A {
    public void fun1()
    {
        Console.WriteLine("Function 1");
    }
}
 
class B : A
{
    public void fun2()
    {
        Console.WriteLine("Function 2");
    }
}
class Program
{
    public static void Main()
    {
        B fullObject = new B();
        test(fullObject);
         
    }
    public static void test(A notSlicedObject)
    {
        ((B)notSlicedObject).fun2();
    }
    
}


Output

Function 2

Note: Casting the reference type didn’t change the type of the object itself instead the only change that happened is to the reference to that object. The object itself didn’t have to be sliced off because it never changed.

We only change the type of the reference that is referencing that object, so that when we say the reference is referencing a type A object then the reference has only access to attributes and methods that an object of type A would have, and when we say the reference is referencing a type B object then the reference has only access to attributes and methods that an object of type B would have.

And note that we can only do this type of conversion or casting only to classes that have an inheritance relationship between them like the example above, otherwise you have to explicitly define that conversion.

For more information about user-defined conversion in C# refer this: User-defined Conversion Operators

Why Object Slicing Does not Happen in C#?

A class in C# is a reference type which means when the object is created, enough memory is allocated on the managed heap for that specific object, and the variable holds only a reference to the location of said object.

Example:

C#




// Declaring an object of type MyClass.
MyClass mc = new MyClass();


Here the object is stored on the heap while a reference to that object is stored in the mc variable.

That means when we try to assign a child class instance to a parent class instance what really happens is that we assign a reference value of a child class instance to a reference variable of a parent class type.

And because references stores only the memory address of that object in the heap all references have the same size in memory (typically 4-bytes for 32-bit CPU architecture and 8-bytes for 64-bit), we can easily assign or cast the type of the reference without slicing off the object because we don’t store it’s value directly but instead we use references.

So, when we are dealing with class objects we do not refer to them directly instead we refer to them using references or pointers to objects and that is the reason why in C# object slicing doesn’t happen unlike other programming languages like C++ in which object slicing can happen.



Last Updated : 09 Jun, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads