Click here to Skip to main content
15,881,744 members
Articles / Programming Languages / C#

Hidden Facts of C# Structures in terms of MSIL

Rate me:
Please Sign up or sign in to vote.
3.86/5 (7 votes)
15 Oct 2010CPOL6 min read 17.9K   2   7
Hidden Facts of C# Structures in terms of MSIL

Have you ever thought of a program without using a single structure declared in it? Never thought so, na? I did that already but found it would be impossible to do that, at least it does not make sense as the whole program is internally running on messages passed from one module to another in terms of structures.

Well, well, hold on. If you are thinking that I am talking about custom structures, you have then gone too far around. Actually I am just talking about declaration of ValueTypes. Yes, if you want to do a calculation, by any means you need an integer, and System.Int32 is actually a structure. So for my own program which runs without a single ValueType declared in it turns out to be a sequence of Print statements. Doesn't make sense, huh! Really, even I thought so. Thus I found it is meaningless to have a program which just calls a few library methods (taking away the fact that library methods can also create valuetypes inside it) and prints arbitrary strings on the output window.

Please note: As struct is actually an unit of ValueType, I have used it synonymously in the post.

So What Makes Us Use Structures So Often?

The first and foremost reason why we use structures is that:

  • They are very simple with fast accessibility.
  • It is also created on Thread local stack, which makes it available only to the current scope and destroyed automatically after the call is returned. It doesn't need any external invisible body to clear up the memory allocated henceforth.
  • Most of the primitives have already been declared as Struct.
  • In case of struct we are dealing with the object itself, rather than with the reference of it.
  • Implicit and explicit operators work great with struct and most of them are included already to the API.

I agree, I can name 100 reasons on the same, but let's focus on the topic I have started discussing. Let's make it a bit practical and create a struct for you.

C#
public struct DemoStruct
{
    int DemoIntItem = 0;
    string DemoStringItem = "Abhishek";
    public DemoStruct()
    {
        this.DemoIntItem = 20;
    }
    public DemoStruct(int loadDemoInt)
    {
        this.DemoIntItem = loadDemoInt;
    }

    public override string ToString()
    {
        return string.Format("DemoIntItem : {0} , 
	DemoStringItem : {1}", this.DemoIntItem, this.DemoStringItem);
    }
}

Hmm, just after I wrote this I went on to compile this, and found a nasty error message comes out of the compiler.

What is it? Where I am wrong? Ahh.. After seeing the message, I found that actually the culprit is you cannot have value initializers for a struct as we do have for classes. Yes, from the CLR point of view, it does not allow you to initialize the value of a variable. Then how would you initialize a member in a struct? Should I declare a constructor? If you see the code above, you have already seen that I have tried to declare a constructor for my type DemoStruct, but alas, "Structs cannot contain explicit parameterless constructors". This is weird. I need to at least write a constructor with a parameter in it to initialize the members with my default values.

Does it Mean Structs Already Have a Default Implicit Constructor?

Yes, as far as the MSDN is concerned, structs do implement a default parameterless constructor implicitly for you which automatically initializes the members to its default values. Actually, the fact is, structs do not have it as a mandatory rule to declare it with a new operator. And hence if you do not create an object of structure with a new operator, all the fields will be left unassigned. So the compiler does not need to have a default constructor for you, and hence the implicit constructor is not required at all.
Let's redeclare the same structure again:

C#
public struct DemoStruct
{
    int DemoIntItem;
    string DemoStringItem;
    //public DemoStruct()
    //{
    //    this.DemoIntItem = 20;
    //}
    public DemoStruct(int loadDemoInt)
    {
        this.DemoIntItem = loadDemoInt;     
    }

    public override string ToString()
    {
        return string.Format("DemoIntItem : {0} , 
	DemoStringItem : {1}", this.DemoIntItem, this.DemoStringItem);
    }
}

So after commenting out a few things, it looks good to me. Now if I compile..... holy s***.. it gives me an error again....

So it says, you need to initialize all the members before returning from the constructors. Hmm, that means, if you declare a constructor, you need to reassign everything again. Well, it makes sense to call the default implicit constructor to do this for me.

Let's redesign the structure once again:

C#
public struct DemoStruct
{
    int DemoIntItem;
    string DemoStringItem;
    //public DemoStruct()
    //{
    //    this.DemoIntItem = 20;
    //}
    public DemoStruct(int loadDemoInt)
        :this()
    {
        this.DemoIntItem = loadDemoInt;
    }

    public override string ToString()
    {
        return string.Format("DemoIntItem : {0} , DemoStringItem : {1}", 
		this.DemoIntItem, this.DemoStringItem);
    }
}

Now finally it works. So this() will initialize all the members of the object. We need to initialize them as initializer for structures is not there as it is there for classes.

Let's call the structure:

C#
static void Main(string[] args)
{
    DemoStruct mystruct = new DemoStruct(30);
    Console.WriteLine(mystruct);

    Console.ReadKey();
}

So clearly we call new DemoStruct to create the object of structure DemoStruct. Hence in this case, our own parametrized constructor will be called. We could have also done something like this:

C#
static void Main(string[] args)
{
    DemoStruct mystruct;
    Console.WriteLine(mystruct);

    Console.ReadKey();
}

In this case also, the structure will call the implicit default constructor and get the object automatically.

Difference between a Class and a Structure in Terms of IL

Let us not talk in terms of general differences between a class and a structure. It is very common and talked many times. I will look into the differences in terms of IL. To see the IL, I am going to use ILDASM. Probably this tool is already available with Visual Studio which lets us look into IL. Let's declare a DemoClass with the same structure as that of DemoStruct and see how the MSIL generated looks like.

In the above tree structure, you can see the structure of the two objects. One is for DemoClass which is exactly the same while the other is DemoStruct which is a structure.

Now let's differentiate the two structures one by one.

IL for DemoClass

MSIL
.class public auto ansi beforefieldinit TestConsoleApps.DemoClass
    extends [mscorlib]System.Object
{
    .field private int32 DemoIntItem
    .field private string DemoStringItem

    .method public hidebysig specialname rtspecialname 
	instance void  .ctor(int32 loadDemoInt) cil managed
    {
        // Code size       17 (0x11)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
        IL_0006:  nop
        IL_0007:  nop
        IL_0008:  ldarg.0
        IL_0009:  ldarg.1
        IL_000a:  stfld      int32 TestConsoleApps.DemoClass::DemoIntItem
        IL_000f:  nop
        IL_0010:  ret
    }
}

IL for DemoStruct

MSIL
.class public sequential ansi sealed beforefieldinit TestConsoleApps.DemoStruct
       extends [mscorlib]System.ValueType
{
    .field private int32 DemoIntItem
    .field private string DemoStringItem
    .method public hidebysig specialname rtspecialname 
        instance void  .ctor(int32 loadDemoInt) cil managed
    {
        // Code size       17 (0x11)
        .maxstack  8
        IL_0000:  ldarg.0
        IL_0001:  initobj    TestConsoleApps.DemoStruct
        IL_0007:  nop
        IL_0008:  ldarg.0
        IL_0009:  ldarg.1
        IL_000a:  stfld      int32 TestConsoleApps.DemoStruct::DemoIntItem
        IL_000f:  nop
        IL_0010:  ret
    }
} 

Based on the two ILs above, you can see both the objects produce a public ansi object where one extends System.ValueType while the other (DemoClass) directly extends System.Object. The difference that we could see in the class header is:

  1. DemoClass is declared as auto while DemoStruct is created as sequential. Auto is used to impose the object to have full Garbage collection and also allows the object to allow reducing the size of it when not in use. Sequential objects are aligned with the object memory boundary. Each of them points individually to the memory allowed for it.
  2. DemoStruct is declared as sealed (hence this disallows the struct from being inherited).

On the other hand, if you differentiate the IL for the constructors, you can see only one difference:

The DemoClass uses:

call  instance void System.Object :: ctor()

That means the constructor for Object is called. So basically the System.Object constructor is called and an object from same is created before the variable are initialized.

DemoStruct on the other hand, uses InitObj which actually initializes each member individually with its default.

Quick Look at Calls

Finally if you look into the IL for the calls made from Main method, it looks like:

MSIL
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       45 (0x2d)
  .maxstack  2
  .locals init ([0] valuetype TestConsoleApps.DemoStruct mystruct,
           [1] class TestConsoleApps.DemoClass myclass)
  IL_0000:  nop
  IL_0001:  ldloca.s   mystruct
  IL_0003:  ldc.i4.s   30
  IL_0005:  call       instance void TestConsoleApps.DemoStruct::.ctor(int32)
  IL_000a:  nop
  IL_000b:  ldc.i4.s   30
  IL_000d:  newobj     instance void TestConsoleApps.DemoClass::.ctor(int32)
  IL_0012:  stloc.1
  IL_0013:  ldloc.0
  IL_0014:  box        TestConsoleApps.DemoStruct
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_001e:  nop
  IL_001f:  ldloc.1
  IL_0020:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0025:  nop
  IL_0026:  call       valuetype [mscorlib]System.ConsoleKeyInfo 
			[mscorlib]System.Console::ReadKey()
  IL_002b:  pop
  IL_002c:  ret
}

Here the entrypoint is declared for the Main method. It creates a local instance for both mystruct and myclass but uses call to call .ctor of structure while using newobj for class. newObj is used to instantiate a new ValueType to hold the reference of the object created. Thus the reference will be stored in stack.

Conclusion

To conclude, structure in C# is basically similar to classes but with restrictions imposed in them to work the best with ValueTypes. Features like inheritance, parameterless constructors, initializers are intentionally revoked from struct to work the best for ValueTypes and with P/Invoke statements. It was fun writing this post. I hope you have liked it too.

Thanks for reading!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
President
India India
Did you like his post?

Oh, lets go a bit further to know him better.
Visit his Website : www.abhisheksur.com to know more about Abhishek.

Abhishek also authored a book on .NET 4.5 Features and recommends you to read it, you will learn a lot from it.
http://bit.ly/EXPERTCookBook

Basically he is from India, who loves to explore the .NET world. He loves to code and in his leisure you always find him talking about technical stuffs.

Working as a VP product of APPSeCONNECT, an integration platform of future, he does all sort of innovation around the product.

Have any problem? Write to him in his Forum.

You can also mail him directly to abhi2434@yahoo.com

Want a Coder like him for your project?
Drop him a mail to contact@abhisheksur.com

Visit His Blog

Dotnet Tricks and Tips



Dont forget to vote or share your comments about his Writing

Comments and Discussions

 
GeneralNice Article! Pin
RAND 45586620-Oct-10 20:26
RAND 45586620-Oct-10 20:26 
GeneralRe: Nice Article! Pin
Abhishek Sur24-Oct-10 11:54
professionalAbhishek Sur24-Oct-10 11:54 
GeneralInt32 and other simple types are not really the structs :) Pin
Viktor Stolbovoy19-Oct-10 8:13
Viktor Stolbovoy19-Oct-10 8:13 
GeneralMy vote of 2 Pin
Qwertie18-Oct-10 15:03
Qwertie18-Oct-10 15:03 
GeneralMy vote of 2 Pin
Crawfis18-Oct-10 10:03
Crawfis18-Oct-10 10:03 
JokeRe: My vote of 2 Pin
tec-goblin18-Oct-10 11:19
tec-goblin18-Oct-10 11:19 
GeneralInformative article Pin
InfRes18-Oct-10 9:14
InfRes18-Oct-10 9:14 

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.