Understanding C# Structures and MSIL

No.of Views1670
Bookmarked0 times
Downloads 
Votes0
By  abhi2434   On  24 Dec 2010 04:12:33
Tag : .NET Frameworks , General
In this article I will explain about how C# strut are working with MSIL in Operating system level And what are advantages and disadvantages using structure in application.
emailbookmarkadd commentsprint

Images in this article missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at info@codegain.com

 

Introduction

In this article I will explain about how C# strut are working with MSIL in Operating system level And what are advantages and disadvantages using structure in application.Now let's ask a question 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.

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# Code

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. 

Image Loading

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# Code

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.

Image Loading

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# Code

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# Code

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# Code

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. 

Image Loading

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

.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

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

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

Download Sample Project

Download source files -23 kb

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!

 

 
Sign Up to vote for this article
 
About Author
 
abhi2434
Occupation-Not Provided
Company-Not Provided
Member Type-Senior
Location-Not Provided
Joined date-22 Oct 2009
Home Page-Not Provided
Blog Page-Not Provided
 
 
Other popularSectionarticles
Comments
There is no comments for this articles.
Leave a Reply
Title:
Display Name:
Email:
(not display in page for the security purphase)
Website:
Message:
Please refresh your screen using Ctrl+F5
If you can't read this number refresh your screen
Please input the anti-spam code that you can read in the image.
^ Scroll to Top
</