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