Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

How Boxing and Unboxing Works – Simulation Model

5.00/5 (2 votes)
10 Apr 2016MIT6 min read 13.1K   65  
One new method of explaining of boxing and unboxing

Introduction

First of all, the purpose of writing this article should be noted. Experienced programmers understand how boxing and unboxing work, as well as how to solve the possible problems associated with these processes. However, successful explanation of these processes to beginners requires them to understand additional technologies. Thus, IL, stack, heap for example, may be hard to understand for them.

Developers should understand boxing and unboxing to avoid negative side effects that can appear in specific situations in software development for .NET platform. Boxing and unboxing can increase memory consumption and reduce performance. Moreover, these processes are often hidden and the programmer cannot control them explicitly.

There are tons of good explanatory materials on the internet and in books. This article is a trial to create one new additional conception how to explain to beginners mechanism of boxing and unboxing. The idea is to make simulation using simple C# code.

It is proposed to create a program that simulates the process of boxing and unboxing for a clear and explicit level. Although the creation of a model that is completely compliant to reality is impossible, the model that can show the key features of considered processes can be built quite easily. By its nature, simulation simplifies the actual situation and, therefore, discrepancy with reality in many cases is not a flaw.

COM Technology

C# OOP and variable model are influenced by previously developed COM model. Although COM is not an subject of this article, some features of COM should be mentioned:

  • COM is a universal OOP supporting technology.
  • COM does not depend on implementation (or usage) programming language. Thus COM has binary format.
  • COM has binary format mirroring internal representation of C++ class. So, COM components are classes, not structures (while classes and structures are quite similar in C++, they are not the same).
  • COM components are accessed only by interfaces.
  • COM components support inheritance by containment and aggregation.
  • COM components support polymorphism through interfaces.
  • COM are binary constructions and their internals cannot be accessed nor modified.

Influence of COM to C# OOP and variable model are:

  • Classes can be accessed only by reference or interface.
  • Structures are not classes. Because pointers are prohibited in safe code, structures are accessed directly, but not by reference or interface.
  • Structures can be wrapped to classes (to object). These wrappers are objects and can be accessed by reference or interface. This magic names boxing. Wrapped structures can be unwrapped, in other words, unboxed back to structures.
  • Structures provide higher performance and smaller memory usage. Classes in their turn supports OOP/OOD. Boxing/Unboxing can be considered as the bridge connecting these two constructions and possibilities.

For more details, you can also see the following materials:

  1. Inheritance, Aggregation, and Containment
  2. “Programming Visual C++”, Microsoft Press, 1998

Simulation

As Wikipedia says: “Simulation is the imitation of the operation of a real-world process or system over time. The act of simulating something first requires that a model be developed; this model represents the key characteristics or behaviors/functions of the selected physical or abstract system or process. The model represents the system itself, whereas the simulation represents the operation of the system over time.” (see Simulation https://en.wikipedia.org/wiki/Simulation).

To understand why we need a model, we should pay attention that boxing and unboxing are internal .NET processes and cannot be controlled by the programmer directly. Programmer can control when boxing or unboxing take place only implicitly by using or avoiding certain C# codes. Thus, the main purpose of the simulation is to show hidden internal behavior according to C# code used.

Model of the System

The simple model can be built to simulate boxing and unboxing of type int. Type int is selected for modeling because it is the widely used simple type. Another type to be modeled is object.

Boxing and unboxing are controlled and implemented by Compiler and Runtime. Compiler and Runtime perform some magic supporting boxing and unboxing. All the details should not be modeled because it is a very complicated task. At the same time, the magic of Compiler and Runtime can be explained by difference of standard code flow and the simulation (see chapter Use Cases below).

The system with these assumptions will contains three components:

  • Model of int type
  • Model of object type
  • Compiler and Runtime – will not be modeled to avoid too complicated model

Model of int

Because int is a value type, let’s create type IntValue that is also structure. Type IntValue will model type int. The code contains comments. See Use Cases below to better understand how to use the code.

At the same time, the following features should be highlighted:

  • At the low level, the structures are located in stack (or in CPU registers if optimized by the JIT), but not in heap. The structures are also accessed directly, but not by reference (thus, they are named as value types). So the structures cannot implement interfaces. Thus, we implemented method CompareTo as a simple member. (We cannot implement it as an IComparable.CompareTo here because it will break the simulation model and put boxing to the compiler magic).
C#
// Models type int for int boxing/unboxing
// Mimics behaviour of int
public struct IntValue
{
    // Stores actual value
    private int _v;

    // Constructs the structure
    public IntValue(int value)
    {
        _v = value;
    }

    // Implicitly converts int to IntValue
    // Need to compile lines such as
    // IntValue a = 83;
    // int b = 21;
    // IntValue c = b;
    public static implicit operator IntValue(int value)
    {
        return new IntValue(value);
    }

    // Implicitly converts IntValue to int
    // Need to compile lines such as
    // IntValue a = 83;
    // int b = a;
    public static implicit operator int(IntValue value)
    {
        return value._v;
    }

    // Output value to string
    public override string ToString()
    {
        return _v.ToString();
    }

    // Compares value to obj
    // Is implemented as a single method
    // but not as a method of IComparable
    public int CompareTo(object obj)
    {
        if (obj == null)
            return 1;

        if (obj is IntValue)
        {
            IntValue iv = (IntValue)obj;
            if (_v < iv) return -1;
            if (_v > iv) return 1;
            return 0;
        }

        throw new System.ArgumentException("Type must be compatible with IntValue");
    }
}

Model of Object

Because object is a reference type, let’s create type IntObject that is a class. Type IntObject models type int that boxed to object. The code contains comments. See Use Cases below to better understand the code.

At the same time, the following features should be highlighted:

  • Simulation of boxing is modeled as conversion from IntValue to IntObject. Because boxing is an implicit process, it is modeled by implicit operator.
  • Simulation of unboxing is modeled as conversion from IntObject to IntValue. Because unboxing is an explicit process, it is modeled by explicit operator.
  • Boxed types internally (at low level) are constructions of object type. But they return type of wrapped values. So GetType() implements this logic.
  • Classes are located in heap and accessed by the reference. Thus, they can implement and provides interfaces natively (see COM Technology above). By this way, IntObject is implemented in a standard way through IComparable.CompareTo calling simple member CompareTo of IntValue.

IntObject specializes working with int type only. This limitation simplifies the model. Although object is universal for all value types, we could also build universal model for object, but it will be too complex to develop and explain boxing and unboxing.

C#
// Models type Object for int boxing/unboxing modelling
// Is wrapper for type IntValue that models int
public class IntObject: IComparable
{
    // Wrapped value type
    private IntValue _v;

    // Constructor from IntValue is private
    private IntObject(IntValue value)
    {
        _v = value;
    }

    // Conversion from IntValue to IntObject
    // mimics boxing (implicit process).
    public static implicit operator IntObject(IntValue value)
    {
        Console.WriteLine("Boxing " + value.ToString());
        return new IntObject(value);
    }

    // Conversion from IntObject to IntValue
    // mimics unboxing (explicit process).
    public static explicit operator IntValue(IntObject value)
    {
        Console.WriteLine("UnBoxing " + value._v.ToString());
        return value._v;
    }

    // Returns Type of wrapped type
    public Type GetType()
    {
        return _v.GetType();
    }

    // Implementation of IComparable.CompareTo method
    public int CompareTo(object obj)
    {
        return _v.CompareTo(obj);
    }
}

Use Cases

Boxing and unboxing can be studied by the following technique:

  1. Replace all int entries by IntValue
  2. Replace all object entries by IntObject
  3. Run the code or trace it line by line in Debug mode

Use Case 1

Simple Boxing and unboxing can be implementing by the following code:

C#
int i = 9;
object o = i;
int i2 = (int)o;

The code can be modeled as:

C#
IntValue i = 9;
IntObject o = i;
IntValue i2 = (IntValue)o;

Output:

Boxing 9
UnBoxing 9

Use Case 2

Boxing and unboxing when use containers can be implemented by the following code:

C#
List<object> l = new List<object>();
for (int ii = 0; ii < 10; ii++)
    l.Add(ii);

int sum = 0;
for (int ii = 0; ii < l.Count; ii++)
    sum += (int)l[ii];

Console.WriteLine(sum);

The code can be modeled as:

C#
List<IntObject> l = new List<IntObject>();
for (IntValue ii = 0; ii < 10; ii++)
    l.Add(ii);

int sum = 0;
for (IntValue ii = 0; ii < l.Count; ii++)
    sum += (IntValue)l[ii];

Console.WriteLine(sum);

Output:

Boxing 0
Boxing 1
Boxing 2
Boxing 3
Boxing 4
Boxing 5
Boxing 6
Boxing 7
Boxing 8
Boxing 9
UnBoxing 0
UnBoxing 1
UnBoxing 2
UnBoxing 3
UnBoxing 4
UnBoxing 5
UnBoxing 6
UnBoxing 7
UnBoxing 8
UnBoxing 9
45

Use Case 3

Studying types of original and boxed values can be implementing by the following code:

C#
int i3 = 37;
object o3 = i3;
Console.WriteLine(i3.GetType() + " " + o3.GetType());

The code can be modeled as:

C#
IntValue i3 = 37;
IntObject o3 = i3;
Console.WriteLine(i3.GetType() + " " + o3.GetType());

Output:

Boxing 37
BoxingModel.IntValue BoxingModel.IntValue

Use Case 4

Studying how taking interface from original type can be implementing by the following code:

C#
int i4a = 495;
IComparable ic4 = (IComparable)i4a;
int i4b = 384;
Console.WriteLine("495 compared to {0} gives {1}", i4b, ic4.CompareTo(i4b));

The code can be modeled as:

C#
IntValue i4a = 495;
// IComparable ic4 = (IComparable)i4a; // Cannot directly get interface from structure
IComparable ic4 = (IntObject)i4a;  // Boxing needs. Compiler implements this magic implicitly.
IntValue i4b = 384;
Console.WriteLine("495 compared to {0} gives {1}", i4b, ic4.CompareTo(i4b));

Output:

Boxing 495
495 compared to 384 gives 1

Conclusion

The model is not ideal, but the main idea is to introduce a new method of teaching hidden implicit processes by modelling by programming language constructions of higher abstraction. The model can be extended including explanation of additional features and use cases.

License

This article, along with any associated source code and files, is licensed under The MIT License