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

Boxing and the Unified Type System

0.00/5 (No votes)
17 Jul 2015CPOL 6.7K  
Boxing and the Unified Type System

In C#, all types inherit from a single root, the object type. This allows for primitive types such as int, boolean, and char to be addressed as objects throughout the system. There is some debate as to whether or not this is a good thing as there is some overhead involved in packaging up or “boxing” primitive types. Other languages, such as Java, don’t require boxing primitive types into reference types, thus trading overhead for convenience. However, in the .NET Framework, boxing primitive types is standard practice.

I was curious as to how much overhead is involved in boxing and unboxing primitive, value types in and out of the Object reference type, so I wrote this small test program.

C#
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace BoxingAndGenerics
{
 class Program
 {
 // This test shows that there is significant overhead when boxing and unboxing value
 // types (int, string, double) into the Object type.
 // All explicit variable declarations have been left in the code instead of using var for clarity
 static void Main()
 {
   const int numberOfInterations = 8000000;

   // create an ArrayList of strings
   var startTime = Stopwatch.GetTimestamp();
   ArrayList aListOfStrings = new ArrayList();
   for (var i = 0; i< numberOfInterations; i++)
   {
     string theString = i.ToString();
     aListOfStrings.Add(theString);

    string unpackedString = (string)aListOfStrings[i];
   }
   var ellapsedTime = (Stopwatch.GetTimestamp() - startTime) / (double) Stopwatch.Frequency;
   Console.WriteLine("Time to create string array with boxing: {0}", ellapsedTime);

   // create a List<String> of strings
   startTime = Stopwatch.GetTimestamp();
   List<string> aGenericListOfStrings = new List<string>();
   for (var i = 0; i < numberOfInterations; i++)
   {
     string theString = i.ToString();
     aGenericListOfStrings.Add(theString);

     string unpackedString = (string)aGenericListOfStrings[i];
   }
   ellapsedTime = (Stopwatch.GetTimestamp() - startTime) / (double)Stopwatch.Frequency;
   Console.WriteLine("Time to create string array using Generic array: {0}", ellapsedTime);


   // create an ArrayList of ints
   startTime = Stopwatch.GetTimestamp();
   ArrayList aListOfInts = new ArrayList();
   for (var i = 0; i < numberOfInterations; i++)
   {
      aListOfInts.Add(i);

      int unpackedInt = (int)aListOfInts[i];
   }
   ellapsedTime = (Stopwatch.GetTimestamp() - startTime) / (double)Stopwatch.Frequency;
   Console.WriteLine("Time to create int array with boxing: {0}", ellapsedTime);

   // create a List<int> of ints
   startTime = Stopwatch.GetTimestamp();
   List<int> aGenericListOfInts = new List<int>();
   for (var i = 0; i < numberOfInterations; i++)
   {
     aGenericListOfInts.Add(i);

     int unpackedInt = (int)aGenericListOfInts[i];
   }
   ellapsedTime = (Stopwatch.GetTimestamp() - startTime) / (double)Stopwatch.Frequency;
   Console.WriteLine("Time to create int array using Generic array: {0}", ellapsedTime);


   // create an ArrayList of doubles
   startTime = Stopwatch.GetTimestamp();
   ArrayList aListOfDoubles = new ArrayList();
   for (var i = 0; i < numberOfInterations; i++)
   {
     double theDouble = (double) i;
     aListOfDoubles.Add(theDouble);

     double unpackedDouble = (double)aListOfDoubles[i];
   }
   ellapsedTime = (Stopwatch.GetTimestamp() - startTime) / (double)Stopwatch.Frequency;
   Console.WriteLine("Time to create double array with boxing: {0}", ellapsedTime);

   // create a List<int> of doubles
   startTime = Stopwatch.GetTimestamp();
   List<double> aGenericListOfDoubles = new List<double>();
   for (var i = 0; i < numberOfInterations; i++)
   {
     double theDouble = (double) i;
     aGenericListOfDoubles.Add(theDouble);

     double unpackedDouble = (double)aGenericListOfDoubles[i];
   }
   ellapsedTime = (Stopwatch.GetTimestamp() - startTime) / (double)Stopwatch.Frequency;
   Console.WriteLine("Time to create double array using Generic array: {0}", ellapsedTime);

   // Put in a read so that we can take a look at the output
   Console.ReadLine();
  }
 }
}

After running the program, this is the output:

Boxing and Generics Output

As expected, strings are always going to be expensive to deal with due to their immutable nature. Interestingly, you can see that numeric types are about 10 times slower when dealing with boxing. Therefore, when possible we should use generic collection types so that the CLR knows in advance the type that it is dealing with and can avoid the overhead of boxing.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)