Common Intermediate Language

C++ vs C#

In this part we will look at the differences between what the C++/CLI compiler generates and that the C# compiler generates with regards to MSIL.

We won't be creating any MSIL applications this time, but rather we will be analyzing the MSIL.

C++/CLI

I really like C++/CLI, it brings the best of both worlds - managed and unmanaged and allows the developer to jump in and out of each world seamlessly.  I would like to point out that C++/CLI and C++ (and C) are more tailored towards system development rather than application development which are more C# and VB.NET space.

Note:  the next version of C++ will include a garbage collector that will be turned off by default.

For this part I'm going to use two examples, a Person class that will be the same in C# as C++ although in C++ I will use get/set methods instead of properties (although properties are get_ and set_ methods after the compiler has worked it's magic).

Person.h

#pragma once

ref class Person
{
public:
	Person(void);
	Person(System::String^ firstName, System::String^ lastName);
	System::String^ GetFirstName(void);
	void SetFirstName(System::String^ firstName);
	System::String^ GetLastName(void);
	void SetLastName(System::String^ lastName);
	virtual System::String^ ToString(void) override;
private:
	System::String^ m_firstName;
	System::String^ m_lastName;
};

Person.cpp

#include "StdAfx.h"
#include "Person.h"

using namespace System;

Person::Person(void)
{
	m_firstName = System::String::Empty;
	m_lastName = System::String::Empty;
}

Person::Person(System::String^ firstName, System::String^ lastName) 
{
	m_firstName = firstName;
	m_lastName = lastName;
}

System::String^ Person::GetFirstName(void)
{
	return m_firstName;
}

void Person::SetFirstName(System::String^ firstName)
{
	m_firstName = firstName;
}

System::String^ Person::GetLastName(void) 
{
	return m_lastName;
}

void Person::SetLastName(System::String^ lastName)
{
	m_lastName = lastName;
}

System::String^ Person::ToString(void)
{
	return m_firstName + " " + m_lastName;
}

We will create a similar Person class in C# now then compare the MSIL generated for a few methods.

C#

using System;

namespace MsilTest
{
    public class Person
    {
        private string _firstName;
        private string _lastName;

        public Person(string firstName, string lastName)
        {

            _firstName = firstName;
            _lastName = lastName;
        }

        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }

        public override string ToString()
        {
            return _firstName + " " + _lastName;
        }

    }
}

The comparison

We will look at the MSIL generated first for the ToString() method.

C++/CLI

.method public hidebysig virtual instance string 
        ToString() cil managed
{
  // Code size       30 (0x1e)
  .maxstack  2
  .locals ([0] string V_0)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      string Person::m_firstName
  IL_0006:  ldstr      " "
  IL_000b:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0010:  ldarg.0
  IL_0011:  ldfld      string Person::m_lastName
  IL_0016:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_001b:  stloc.0
  IL_001c:  ldloc.0
  IL_001d:  ret
} // end of method Person::ToString

C#

.method public hidebysig virtual instance string 
        ToString() cil managed
{
  // Code size       28 (0x1c)
  .maxstack  3
  .locals init ([0] string CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldfld      string MsilTest.Person::_firstName
  IL_0007:  ldstr      " "
  IL_000c:  ldarg.0
  IL_000d:  ldfld      string MsilTest.Person::_lastName
  IL_0012:  call       string [mscorlib]System.String::Concat(string,
                                                              string,
                                                              string)
  IL_0017:  stloc.0
  IL_0018:  br.s       IL_001a
  IL_001a:  ldloc.0
  IL_001b:  ret
} // end of method Person::ToString

There's not much difference at all between the C++/CLI and C# implementation - the C# compiler tells the CLR it wants 3 slots on the stack max because it uses the Concat method which takes 3 args thus 3 strings are pushed onto the stack prior to the method call where as the C++/CLI implementation calls the Concat method twice using the 2 arg version, hence the maxstack being 2.

Note:  both binaries are debug releases and are not optimized.

In the C# MSIL there is a nop instruction this is used for debugging, e.g. breakpoints, and stands for "no operation".

You might also like...

Comments

About the author

Granville Barnettt United Kingdom

My name is Granville Barnett I have been a programmer now for quite some time mainly focusing on .NET technologies (C#), C++ and general research (algorithms, compilers etc)

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Before software should be reusable, it should be usable.” - Ralph Johnson