Click here to Skip to main content
15,897,891 members
Articles / Programming Languages / MSIL
Article

VB vs. C# MSIL Code Generation: Are the results equal?

Rate me:
Please Sign up or sign in to vote.
4.60/5 (38 votes)
7 Feb 200511 min read 289.9K   322   32   106
A discussion of some differences between VB and C# MSIL code.

Introduction

The intent of this article is to once and for all prove, by means of empirical evidence, that the MSIL code generated by the VB and C# compilers are not equal, particularly when dealing with non void methods and string value comparisons. In order to prove this point, a contrast will be made between the MSIL code produced by each compiler for two identical assemblies, one coded in VB, the other in C#, with possible explanations as to why the differences exist. Afterwards, the significance of this discrepancy will be briefly discussed along with some biased comments intended to flare up the VB vs. C# language wars.

Test Assemblies

The two assemblies used in the test are identical in all respects. However, here I only provide the code for the one and only type each assembly exposes, as well as the resulting MSIL code representing the only two methods the identical types expose. To verify that “other factors” are equal as well, such as optimization settings, feel free to download the code. Finally, both assemblies were coded and compiled using VS.NET 2003 (.NET 1.1 SP1) on a Windows XP SP2 machine. Moreover, the MSIL code was obtained using ildasm.exe (version 1.1.4322.573).

VB type ClassLibrary1.Class1:

VB
Public Class Class1

    Public Sub New()

    End Sub

    Public Function foo1(ByVal test As Boolean) As Integer
        Dim i As Integer
        If (test) Then
            i = 1
        Else
            i = 2
        End If
        Return i
    End Function

    Public Sub foo2(ByVal test As Boolean)
        Dim s As String = Nothing
        If s = String.Empty Then
        End If
    End Sub

End Class

C# type ClassLibrary1.Class1:

C#
public class Class1
{
    public Class1()
    {

    }

    public int foo1(bool test)
    {
        int i;
        if(test)
            i=1;
        else
            i=2;
        return i;
    }

    public void foo2(bool test)
    {
        string s = null;
        if(s == string.Empty){};
    }
}

So now we have two identical types, the former written in VB and the latter written in C#. First, let’s compare the MSIL code each compiler produces for member foo1.

VB MSIL (foo1):

MSIL
.method public instance int32  foo1(bool test) cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  ldarg.1
  IL_0001:  brfalse.s  IL_0007
  IL_0003:  ldc.i4.1
  IL_0004:  stloc.1
  IL_0005:  br.s       IL_0009
  IL_0007:  ldc.i4.2
  IL_0008:  stloc.1
  IL_0009:  ldloc.1
  IL_000a:  ret
} // end of method Class1::foo1

C# MSIL (foo1):

MSIL
.method public hidebysig instance int32  foo1(bool test) cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init (int32 V_0)
  IL_0000:  ldarg.1
  IL_0001:  brfalse.s  IL_0007
  IL_0003:  ldc.i4.1
  IL_0004:  stloc.0
  IL_0005:  br.s       IL_0009
  IL_0007:  ldc.i4.2
  IL_0008:  stloc.0
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method Class1::foo1

At first glance, the MSIL code produced by both compilers looks identical. First, each version has the same number of MSIL code lines as the other. Second, both versions have identical .maxstack directives, that is, each method expects to use the same number of stack slots as the other. However, if you look at the third line of MSIL code, the .locals init directive, which is used to declare variables and assign initial values to them, a striking difference exists between both versions:

VB: .locals init (int32 V_0,int32 V_1)

C#: .locals init (int32 V_0)

Why is it that, despite the fact that method foo1 makes use of one, and only one, local variable (i), the VB MSIL code declares and initializes an extra variable, variable V_0, one which is never used? On the other hand, the C# MSIL code declares and initializes one, and only one, variable, which makes absolute sense for the obvious reasons already stated.

Now the question becomes, why the discrepancy, regardless of how significant or insignificant it may be. I’m no expert, especially when dealing with MSIL, yet I am almost 100% certain that the unused variable is there only because, unlike C#, VB allows a function’s code path to not return a value, and when this happens, the unused V_0 variable becomes used. In other words, in VB, you can have a function with a certain return type, yet if you do not explicitly return a value of that type, the compiler will return the type’s default value for you, hence, the existence of the mysterious V_0 variable. To prove my point, let’s comment out the last line of the VB version of the foo1 method, the line that explicitly returns the function’s value.

VB
Public Function foo1(ByVal test As Boolean) As Integer
      Dim i As Integer
      If (test) Then
           i = 1
      Else
          i = 2
      End If
       'Return i
End Function

After the above change is made and compiled, the resulting MSIL code looks like:

MSIL
.method public instance int32 foo1(bool test) cil managed
{
  // Code size       11 (0xb)
  .maxstack  1
  .locals init (int32 V_0,
           int32 V_1)
  IL_0000:  ldarg.1
  IL_0001:  brfalse.s  IL_0007
  IL_0003:  ldc.i4.1
  IL_0004:  stloc.1
  IL_0005:  br.s       IL_0009
  IL_0007:  ldc.i4.2
  IL_0008:  stloc.1
  IL_0009:  ldloc.0
  IL_000a:  ret
} // end of method Class1::foo1

The only difference between both versions of the VB foo1 MSIL code is in the second to last line. The first version, which explicitly returns a value, pushes variable V_1 onto the stack, via ldloc.1, since V_1 represents variable i, which in turn holds the value that is explicitly returned. Contrast that to the second version, which doesn’t explicitly return a value and, thereby, results in the compiler pushing variable V_0, which always holds the default value of the function’s return type, onto the stack by means of instruction ldloc.0.

So the moral of the story here is that for any non void method written in VB, the compiler will generate MSIL code that always declares and initializes an extra local variable of the same type as its containing function, a variable which may or may not be used, respectively depending on whether the function does not explicitly return a value. My personal opinion here is that, although the extra variable is insignificant with respect to its impact on performance and memory consumption, nonetheless the VB compiler should be smart enough to include the extra variable if, and only if, the member’s code path does not return a value. If it does, then the declaration and initialization of the extra variable is without question pointless.

Now, let’s move on and take a look at the MSIL code the VB and C# compilers generated for method foo2.

VB MSIL (foo2):

MSIL
.method public instance void  foo2(bool test) cil managed
{
  // Code size       18 (0x12)
  .maxstack  3
  .locals init (string V_0)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldsfld     string [mscorlib]System.String::Empty
  IL_0008:  ldc.i4.0
  IL_0009:  call       int32 [Microsoft.VisualBasic]Microsoft.VisualBasic.
                                 CompilerServices.StringType::StrCmp(string,
                                 string,
                                 bool)
  IL_000e:  ldc.i4.0
  IL_000f:  bne.un.s   IL_0011
  IL_0011:  ret
} // end of method Class1::foo2

C# MSIL (foo2):

MSIL
.method public hidebysig instance void  foo2(bool test) cil managed
{
  // Code size       15 (0xf)
  .maxstack  2
  .locals init (string V_0)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldsfld     string [mscorlib]System.String::Empty
  IL_0008:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_000d:  pop
  IL_000e:  ret
} // end of method Class1::foo2

Well, it’s obvious with just a quick glance of the two versions of MSIL code generated for method foo2 that differences do exist, moreover, much more obvious differences than was the case with foo1.

First, comparing the code size comments for each version, it’s clear that more MSIL code is involved with the VB version (//Code size 18 (0x12)) than with the C# version (//Code size 15 (0xf)).

Second, the VB version expects and will use three stack slots (.maxstack 3), while the C# version expects and will use only two stack slots (.maxstack 2).

From there on, all remains equal until we reach line IL_0008, at which point both versions drastically diverge.

The C# version first pushes onto the stack the Boolean result of the call made to [mscorlib]System.String::op_Equality, which is the function C# uses to compare string values when using the == operator. Then, it immediately removes (pop) this same Boolean value from the stack right before returning execution to the caller. Although the last pop seems pointless, there’s really no choice in the matter because a method’s evaluation stack must be left empty before returning control to the caller, unless a value is to be returned, as is the case with non void methods.

On the other hand, the VB version first pushes onto the stack the value 0 (ldc.i4.0), which will subsequently be popped off the stack as an argument to the call made to Microsoft.VisualBasic.CompilerServices.StringType::StrCmp, which is the function VB uses to compare string values when using the = operator, the Integer result of which is pushed onto the stack. Once that’s done, instruction ldc.i4.0 is once again used to push the value 0 onto the stack so that it may be subsequently compared to the Integer result of StrComp. Then, by means of instruction bne.un.s IL_0011, the last two values that were pushed onto the stack, that is, the results of the calls made to StrCmp and ldc.i4.0, are popped off, compared, and if inequality results, execution transfers to instruction IL_0011, which seems redundant since instruction IL_0011 follows immediately regardless of whether or not the result is inequality, yet most likely is used in order to pop off the stack the last two values that still remain on it.

So once again the question becomes, why the discrepancy, regardless of how significant or insignificant it may be. Well, in this case, the answer is clear and simple. For backwards compatibility reasons, VB is forced to use StrCmp instead of String::op_Equality, the results of which are quite different, with the more drastic one being that StrCmp considers an empty string and a Null string equal, which is not the case with String::op_Equality, and, furthermore, this is most likely the main reason why the former is used instead of the latter. From a pure performance standpoint, String::op_Equality is superior to StrCmp, although the performance gains will manifest themselves only when an intense number of string comparisons are made, perhaps those made inside a tight loop. Furthermore, there is an easy workaround which will eliminate this problem in cases where maximum performance is a must, and that is to use String.Equals or op_Equality instead of the = operator when comparing string values. For example, changing the VB version of foo2 to:

VB
Public Sub foo2(ByVal test As Boolean)
    Dim s As String = Nothing
    If String.op_Equality(s, String.Empty) Then
    End If
End Sub

results in the following MSIL code:

MSIL
.method public instance void  foo2(bool test) cil managed
{
  // Code size       16 (0x10)
  .maxstack  2
  .locals init (string V_0)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldsfld     string [mscorlib]System.String::Empty
  IL_0008:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_000d:  brfalse.s  IL_000f
  IL_000f:  ret
} // end of method Class1::foo2

Note that now both versions of the MSIL code are identical, except for the second to last instruction. The C# version simply pops off the stack the last value that remains on it, via the pop instruction, whereas the VB version pops off the last value on the stack, compares it to zero, and if equality exists, transfers control to instruction IL_000f, all via the brfalse.s L_000f instruction, which in this case seems to be redundant, since the original if statement block is empty and therefore the compiler should be smart enough to ignore it, although on the other hand, the compiler is also smart enough to know that under most, if not all, circumstances, an if statement block will not be empty and, therefore, probably figures why even bother trying to detect one.

Significance of Test Results

You probably have come to the conclusion after having read this article that I am a C# programmer who is trying to prove a point. Well, you’re half right. I’m certainly trying to prove a point, and the point is that, for reasons that have to do with the intrinsic nature of the VB language, how you use the language, backwards compatibility with legacy code, and logical assumptions that result in inaction, the VB compiler generates code that is almost as efficient as the C# compiler but does not generate code that is as a efficient as the C# compiler.

(Note: biased statements intended to promote VB vs. C# language wars are about to begin, so you may want to stop reading.)

Having said that, let me make it quite clear that I do all of my imperative coding in VB, ever since I got my first job after having obtained my BS in MIS. It’s an excellent language that, since version 4 and to a greater extent starting with version 7 (.NET), doesn’t restrict the programmer to a single programming paradigm the way C# does. Regarding the significance of the differences in MSIL code this article focused on, who on earth really cares besides programmers that despise (CG) VB? Certainly, the folks that pay us to code could care less about all the statements made in this article, and I assure you that if I were to try to sell the case that C# is “better” than VB based on what’s in this article to a CIO/CTO that knows his .NET, I would probably be fired for my complete lack of business judgment. Furthermore, go and show Jim, VP of finance, the MSIL code that’s included in this article and try to make the case that all of his department’s VB applications should all be rebuilt in C#, and I guarantee you will be the laughing stock of the season, although some C# folks around here, one in particular, are bold enough to try something like this.

Really, the one and only reason I’ve written this article is just to throw gasoline on the flames of the VB vs. C# wars. I’m sick and tired of hearing all the rhetoric about all .NET languages being equal, because that is just not the case, for VB is without question superior to C#. Furthermore, I also pity all the pathetic premises, ones that are so easily negated, that are used to support the absurd argument that C# is "better" than VB. Things like “it’s too wordy” or “too much bad VB code exists” just does not cut it. If you start a C# vs. VB debate here on CP or anywhere, please make sure to give me the link so that I can participate.

Please don't take all my trash talk seriously. I respect all of you guys. It's just that I have zero respect for the C# language. It's just another RAD tool, nothing more, nothing less, that's been fortunate enough to leverage off the experiences, good and bad, of various other languages, including VB, and, furthermore, doesn't have to worry about existing legacy code. My attitude will likely change the day MS Office is written completely in C#.

Final Thoughts

Finally, I'm fairly confident that most of the conclusions I've made regarding the comparison of MSIL code generation between the C# and VB compilers are true. However, like I already mentioned, I'm no expert, yet if you are and know something I don't or was able to detect errors on my part, please let me know.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: The Danger is in Code Design Pin
J. K. Johnson21-Feb-05 6:19
J. K. Johnson21-Feb-05 6:19 
GeneralRe: The Danger is in Code Design Pin
Giancarlo Aguilera21-Feb-05 6:41
Giancarlo Aguilera21-Feb-05 6:41 
GeneralRe: The Danger is in Code Design Pin
J. K. Johnson21-Feb-05 7:35
J. K. Johnson21-Feb-05 7:35 
GeneralRe: The Danger is in Code Design Pin
Misty_Blue22-Feb-05 10:56
Misty_Blue22-Feb-05 10:56 
GeneralRe: The Danger is in Code Design Pin
Giancarlo Aguilera22-Feb-05 11:49
Giancarlo Aguilera22-Feb-05 11:49 
GeneralRe: The Danger is in Code Design Pin
Giancarlo Aguilera20-Feb-05 21:11
Giancarlo Aguilera20-Feb-05 21:11 
GeneralPlease read. Pin
massimo195717-Feb-05 22:11
massimo195717-Feb-05 22:11 
GeneralRe: Please read. Pin
Giancarlo Aguilera18-Feb-05 6:25
Giancarlo Aguilera18-Feb-05 6:25 
massimo wrote:

"Im am a professional (but not good) VB6 programmer, 48 y.o. , and I'm fighting with the idea to upgrade to .net."

please don't fight it any longer, .net will make your life easier regardless of the language you choose. certainly I am not implying that you MUST use .NET, that all depends. From a pure programming perspective, .NET rocks Big Grin | :-D , and YES you MUST upgrade. However, and this is just my opinion, there's always a business context you MUST take into account before making these type of decisions. For example, a friend of mine from college, who is now an accountant, refuses to look at .NET and remains faithful to VB COM. Well, given his context why should he upgrade? First of all, he does not make a living programming. Second, none of his apps are "mission critical", just a couple of forms and modules (it's not a pretty sight) that do very basic things, and, furthermore, I never understood why he just didn't use Access instead of VB. Third, he could care less about OOP, web services, and a whole bunch of other "out of the .NET box" features. I don't know you so I really can't say for sure that you should upgrade to .NET, however, given the fact that you described yourself as a "professional" programmer, from that I must imply that you make a living programming. Therefore, if this is the case, then YES, by all means do upgrade. In the long run, although not in the short run, your survival may depend on it. And honestly, it's not that bad, rather it's all good, although if I were you, assuming you haven't worked with .NET 1.X yet, I would start playing with the beta 1 version of .NET 2.0 rather than spend the time with the current stuff or maybe wait for beta 2 which is coming out in a couple of weeks and which MS claims is much more stable than beta 1, and believe me beta 1 has all kinds of issues.

massimo wrote:
"Till now I was very unsure in the choice between VB and C#, now I believe that VB is the the right one."

Of course VB is the right one, it blows C# away Laugh | :laugh: . Seriously though, in this article I somewhat trashed C# but my comments were no doubt biased and just a nasty response to all the VB trash lots of folks around here talk. C# is an awesome language, almost as awesome as VB Laugh | :laugh: . Unfortunately, however, and this is just my opinion, in version 2.0 C# will have a pretty significant "language" edge over VB, and if you're interested in what this edge consists of read chapters 21 (anonymous methods), 22 (iterators), and 24 (nullable types) of the C# 2.0 specification. On the other hand, however, rest assured that VB 2005 will make your transition to .NET a whole lot easier than it would be now, one of the main reasons being the beatiful "My" facade, although I don't see myself using it much for purposes other than RAD and UI development. Honestly, one of the main objectives of the VB team for 2005 is to make it easier for VB COM programmers to make the switch, with this objective being so important that it left the VB team without time to include the "language" features I just mentioned. Remember that what makes .NET so damn cool is that you're not forced to use one language. You may want to even follow the trend and make a backwards shift Laugh | :laugh: and learn C# since you already know VB.

massimo wrote:
"It would be very nice if "YOU" could help "US" in this choice and maybe give some tips about the RIGHT books to buy and read.

Have you ever seen one of the many Nike commercials? If not, just do it! Don't think about it so much and just make the move. As far as books are concerned, man there are so many out there it's really hard to choose, but in line with my earlier advice, look for books that will help you get up to speed with VS.NET 2005 (.NET 2.0) and forget about the current stuff. I know right at this moment there aren't many out there that teach 2.0, however, they're sure to arrive by the dozens now that beta 2 is just around the corner, similar to what happened when beta 2 of .NET 1.0 was released. Also, if you really want to get down and dirty with .NET 2.0 I must recommend the c# 2.0 specification, although it's not the easiest reading.

massimo wrote:
"I have another question: I own VB.NET first version and not VB.NET 2003, is this a big problem?

The main difference between the two has to do with the fact that the former uses .NET 1.0 and the latter uses .NET 1.1. Whether or not it makes a difference, well that depends. To me it made a huge difference since my applications MUST be able to run on windows 98, and fortunately .NET 1.1 fixed many but not all windows 98 bugs, especially those relating to printing. And of course finally in 2003 VB was given the ability to declare the for loop counter in line with the for loop itself, a feature long over due, yet at the same time who cares other than folks like myself. In any case, like I've said one hundred times before, since you haven't jumped on the .net bandwagon yet, and this is just my humble opinion, don't waste your time with the current stuff rather jump straight into the new stuff. beta 1 baby!

thanks for taking the time to read the article and I appreciate your feedback
GeneralRe: Please read. Pin
massimo195720-Feb-05 21:24
massimo195720-Feb-05 21:24 
GeneralRe: Please read. Pin
Dag Oystein Johansen20-Aug-07 9:26
Dag Oystein Johansen20-Aug-07 9:26 
GeneralRe: Please read. Pin
cjb11014-May-10 4:03
cjb11014-May-10 4:03 
GeneralWars -- OO Pin
tsmyrnio16-Feb-05 14:09
tsmyrnio16-Feb-05 14:09 
GeneralRe: Wars -- OO Pin
Christian Graus16-Feb-05 14:28
protectorChristian Graus16-Feb-05 14:28 
GeneralRe: Wars -- OO Pin
Giancarlo Aguilera16-Feb-05 15:07
Giancarlo Aguilera16-Feb-05 15:07 
GeneralRe: Wars -- OO Pin
tsmyrnio18-Feb-05 14:11
tsmyrnio18-Feb-05 14:11 
GeneralProblem with VB.NET Pin
Eddie de Bear7-Feb-05 15:19
Eddie de Bear7-Feb-05 15:19 
GeneralRe: Problem with VB.NET Pin
Huisheng Chen24-Feb-05 0:26
Huisheng Chen24-Feb-05 0:26 
GeneralVB.Net Coding Efficiency Pin
Joshua Quick7-Feb-05 11:12
Joshua Quick7-Feb-05 11:12 
GeneralRe: VB.Net Coding Efficiency Pin
Giancarlo Aguilera7-Feb-05 12:41
Giancarlo Aguilera7-Feb-05 12:41 
GeneralRe: VB.Net Coding Efficiency Pin
Misty_Blue22-Feb-05 8:29
Misty_Blue22-Feb-05 8:29 
GeneralRe: VB.Net Coding Efficiency Pin
Giancarlo Aguilera22-Feb-05 8:44
Giancarlo Aguilera22-Feb-05 8:44 
GeneralRe: VB.Net Coding Efficiency Pin
Misty_Blue22-Feb-05 9:22
Misty_Blue22-Feb-05 9:22 
GeneralRe: VB.Net Coding Efficiency Pin
Giancarlo Aguilera22-Feb-05 12:46
Giancarlo Aguilera22-Feb-05 12:46 
GeneralRe: VB.Net Coding Efficiency Pin
Misty_Blue24-Feb-05 9:08
Misty_Blue24-Feb-05 9:08 
GeneralThings have changed.. Pin
Ray Cassick7-Feb-05 10:33
Ray Cassick7-Feb-05 10:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.