Overflows, C# and VB.Net, and IL

Students frequently ask, “What are the differences between C# and VB.Net?”  One of the first things we cover in our beginning .Net classes is how .Net languages like VB.Net and C# both produce MSIL (Microsoft Intermediate Language); IL, in turn, is used to produce the machine instructions for your program.  Most agree that the .Net language you use is more a matter of preference than anything else — in the end, it all produces Intermediate Language code. 


The catch is that IL isn’t exactly the same between C# and VB.Net — each compiler can emit different IL instructions.  We can leverage IL to get insight into the subtle differences between VB.Net and C#. 


Let’s take a simple Windows application with a button and textbox on it; we’ll add a single event handler for the button.Click event like this:
VB (With Option Strict On)
Line 1) dim b as byte = byte.Parse( Textbox1.Text ) ‘convert byte from Textbox’s string
Line 2) b = b + byte.Parse( “200″ ) ‘literals are integers and Option Strict forces our explicit conversion here
Line 3) MessageBox.Show( b.ToString() )


C#
Line 1) byte b = byte.Parse( Textbox1.Text ); //convert byte from Textbox’s string
Line 2) b += 200;
Line 3) MessageBox.Show( b.ToString() );


If you run this code and type the number 1 into the textbox and click your button, you’ll get “201″ displayed in the MessageBox for VB.Net and C#.  If you type a number large enough so we exceed the capacity of a byte in line 2, say “100″, you’ll get an exception in VB and you’ll get “44″ displayed in the C# MessageBox.


This is because by default, C# doesn’t check for mathematical overflows (overflows occur when you exceed the range of a type — a byte can contain values ranging 0-255).  So when we try to squeeze a 300 value into our byte, the first 255 is discarded leaving us with 44.  This can actually be useful; in cases where you’re computing a checksum or hash based on a class, this behaviour can be important.



Digging into this example further, C# offers the checked and unchecked keywords that will set the execution context of the C# compiler.  If we typed:
checked
{
    byte b = byte.Parse( Textbox1.Text );
    b+=200;
    MessageBox.Show( b.ToString() );
}


We would force C# overflow checking for this particular block of code and raise an Overflow exception if we type “100″ into our Textbox. unchecked does the opposite.  C# also offers a project compiler setting from the Project Property Page (right click on the Project in Solution Explorer); it’s in Configuration Properties->Build and look at the Code Generation section.  We can toggle this behaviour on or off via the “Check for Arithmetic Overflow/Underflow” option.


I know some people like to switch this to “True” for C# projects while you’re developing the program, and then switch it to “False” once you release the program — they want to boost the performance of their application when they release it.  I don’t take such a blanket approach; instead, I would pay attention to your C# code for places where arithmetic overflow is a danger and plan accordingly (via checked or unchecked).  Don’t just follow the “checked for developing – unchecked for releasing” maxim unless you’re completely aware of the implications!


I started this post by mentioning IL and this is an interesting opportunity to glance at the IL generated by VB.Net and C#.  Using ILDasm (I discuss ILDasm a bit here or MSDN introduces it here), we can get under the hood of .Net.  Running ILDasm on our VB.Net example yields the following in the Button1_Click method (this snippet is about half way down in the method):
  IL_0018:  call       unsigned int8 [mscorlib]System.Byte::Parse(string)
  IL_001d:  add
  IL_001e:  conv.ovf.u1


Note the “conv.ovf.u1″ instruction; this is IL that forces an arithmetic overflow (ovf) check.  Use ILDasm to inspect the same C# method from our example (no checked statement or compiler option):
  IL_000b:  call       unsigned int8 [mscorlib]System.Byte::Parse(string)
  IL_0010:  stloc.0
  IL_0011:  ldloc.0
  IL_0012:  ldc.i4     0xc8
  IL_0017:  add
  IL_0018:  conv.u1


Note the “conv.u1″ instruction; this is IL that DOES NOT force and arithmetic overflow check.   

[Update: Thanks to Greg Robinson for pointing out my ommision of the “Remove Integer Overflow Checks“ option on the VB.Net Project properties.  This will control the execution context of the VB.Net project like the C# “Check for Arithmetic Overflow/Underflow" option.] 

We can see that the VB.Net and C# compilers produce different IL for roughly the same functionality.  I know of a few people who write some code in pure IL, and skip the higher level languages altogether.  This is much more like writing assembly language code and I’m not into that . . . I am, however, into using ILDasm to explore the nuances of IL as it relates to C# and VB.Net.  Try it out and you’ll learn a lot about your .Net language of choice.


Happy .Netting!

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

7 Responses to Overflows, C# and VB.Net, and IL

  1. Shailesh says:

    Thnaks. It help me to clear some basics.

  2. Kelly says:

    Would some body know how to convert frames/byte format data into data. And vise versa.

    What lib should i be looking at using c# / .net.

    how would u decode bytes into data, and encode data into bytes, e.g engine sensor values to be past via a parallel port from a DSP to a PC.

    Thanx heaps guys…

  3. kik says:

    good stuff

  4. Grant says:

    Good catch Greg, I’ve modified the post.

  5. Greg Robinson says:

    What about the "Remove integer overflow checks" and "Enable Optimization" ?

  6. Grant says:

    Yes, Ryan, you’re right. Convert.ToByte() would be much better than string parsing.

    The literal is an "integer" type in VB.Net and Option Strict On requires some conversion to byte for it to compile; Byte.Parse() probably came to mind because of the Textbox parsing on the line previous.

    This would be a good candidate for Refactoring!

  7. Ryan Gregg says:

    Grant, good article on the subtle differences between VB and C#. However, I’m curious why in your examples you forced a conversion from a literal string to a byte in your VB example, and yet in C# let it happen as actual numerical literals. This should surely cause a performance hit if you did this repeatedly.

    Why not just use this instead:

    b = b + Convert.ToByte(200)

    Since the Convert.ToByte method is overloaded to actually have a Integer to Byte conversion. Even still, using CByte has approximately the same performance as Convert.ToByte, and is still considerably better than string parsing.

    Several sample cases of 10mil operations (averages reported)

    Using CByte: 9.375e-9 seconds per op

    Using Convert.ToByte: 1.562e-8 seconds per op

    Using Byte.Parse: 4.265e-7 seconds per op

    Hopefully this was just an oversight, and not anyone’s standard method of writing out non-integer literals.

Leave a Reply