Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / All-Topics

Internals of Conversion styles in .NET

0.00/5 (No votes)
19 Dec 2011LGPL33 min read 9.1K  
Internals of Conversion styles in .NET

In this post, I would like to share some things about Conversion styles in .NET I learned a few days back. Basically in this case, I am talking about casting any numeric value to an equivalent char type.

There are three ways one can convert one type to another, in this case numeric to char:

  1. Direct Casting
  2. Via Convert helper class
  3. Via IConvertible interface

Let's dig a bit deeper into each of these styles and see its pros and cons if any.

Direct Casting

This conversion style is the most basic and most efficient one. In this style, the compiler need not emit any special OpCodes or method calls. Instead, it just emits basic OpCodes which does the job efficiently. Basically compiler emits conv.u2 or conv.ovf.u2 OpCode instructions for unchecked and checked operations respectively.

Let's see the IL for this operation:

C++
int value = 99;
char c = (char)value;

For the above code, the IL generated is:

MSIL
IL_0000: nop
IL_0001: ldc.i4.s 99
IL_0003: stloc.0
IL_0004: nop
IL_0005: ldloc.0
IL_0006: conv.u2
IL_0007: stloc.1
IL_0008: nop
IL_0009: ret

As you can see, there is no extra complex OpCodes being generated nor any method calls. Only opcode which does the actual conversion is generated at IL_0006 line.

Although this style is the most efficient one, the only drawback of using this style is that it does not support culture specific conversion. In other words, you can’t specify IFormatProvider to the conversion operation.

Convert Helper class

In this style, we use Convert class which is a helper class provided by .NET framework class library (FCL). As you may already know, Convert class is a static class which provides a lot of static methods for conversion operations. Since there is a method call in this scenario, so this is considered to be the second most efficient style among others. The Convert methods internally checks for overflow operation conditions by default. Hence whenever there is overflow occurrence, it throws an exception of type OverFlowException.

So in this case, I have used Convert.ToChar(Int32) API. So the code in FCL for this API looks like below:

C++
public static char ToChar(int value)
{
if (value < 0 || value > 65535)
{
throw new OverflowException(
   Environment.GetResourceString("Overflow_Char"));
}
return (char)value;
}

One important point worth remembering here is that, one needs to be careful while using the API Convert.ToChar(Object, IFormatProvider) with valuetypes. Because internally, it uses IConvertible interface for conversion which results in low performance issues as explained in the next section.

IConvertible Interface

In BCL, many basic types viz, Int32, DateTime, Char, etc. do implement interface IConvertible. Many other types might as well implement this interface, but it’s of no interest here. One important thing to remember here is that this style of conversion is very poor in performance, since in case of valuetypes, the input is first boxed and then conversion is done on it.

Let's see a sample example:

C++
char c = ((IConvertible)95).ToChar(null);

In the above line, Explicit conversion to IConvertible is required, because many types in BCL viz, Char, String, Int32, etc. do implement IConvertible methods explicitly. Looking at the IL for the above code shows the exact problem:

MSIL
IL_0000: nop
IL_0001: ldc.i4.s 95
IL_0003: box [mscorlib]System.Int32
IL_0008: ldnull
IL_0009: callvirt instance char [mscorlib]System.IConvertible::ToChar
	(class [mscorlib]System.IFormatProvider)
IL_000e: stloc.0
IL_000f: ret

As you can see in the IL_0003 instruction, the value is getting boxed. Hence this style of conversion is least efficient of all others. So it's better to avoid this style.

Now let's proceed to do some performance analysis on all three styles, although from the theoretical aspect from the above lines, we know their performance costs but still let's prove it with an example to convince fully. Hence I have used the below code:

C++
static Stopwatch sp = new Stopwatch();
static int counter = 10000000;
static int valueToConvert = 70;

public static void Main()
{
    GC.WaitForPendingFinalizers();
    GC.Collect();
    for (int j = 0; j < 3; j++)
    {
        Console.WriteLine("Casting style...");
        StartTimer();
        for (int i = 0; i < counter; i++)
        {
            Char c = (char)valueToConvert;
        }

        StopTimer();
        PrintElapsedTime();

        Console.WriteLine("Using Convert helper method..");
        StartTimer();
        for (int i = 0; i < counter; i++)
        {
            Char c = Convert.ToChar(valueToConvert);
        }
        StopTimer();
        PrintElapsedTime();

        Console.WriteLine("Via IConvertible interface...");
        StartTimer();
        for (int i = 0; i < counter; i++)
        {
            Char c = ((IConvertible)valueToConvert).ToChar(null);
        }
        StopTimer();
        PrintElapsedTime();

        Console.WriteLine("\n\n");
    }
    Console.ReadLine();
}

As you can see from the above test code, I am taking 3 samples of the performance results just to make sure output is quite accurate. Hence from this, I got the below result:

Casting style…
Time elapsed in ms = 22
Using Convert helper method..
Time elapsed in ms = 60
Via IConvertible interface…
Time elapsed in ms = 233
Casting style…
Time elapsed in ms = 255
Using Convert helper method..
Time elapsed in ms = 294
Via IConvertible interface…
Time elapsed in ms = 417
Casting style…
Time elapsed in ms = 439
Using Convert helper method..
Time elapsed in ms = 478
Via IConvertible interface…
Time elapsed in ms = 598

From the above results, it is very much clear that the performance issues are much better with the direct casting and acceptable with Convert helper methods in this case.

Hope it helps.

Thanks for reading. Your votes/comments are much appreciated.

Happy coding,

Thanks, Zen :)


Filed under: C#, CodeProject, Dotnet Tagged: .NET, blog, blogger, C#, codeproject, Dotnet, tips

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)