Members

Technology Zones

Articles

Hosted By

MaximumASP

Info

Rated
Read 6,065 times

Contents

Related Categories

Common Intermediate Language - C++ vs C#

gbarnett

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".

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)

Comments