Click here to Skip to main content
Click here to Skip to main content

An indepth look at structures in C#

, 19 Nov 2012
Rate this:
Please Sign up or sign in to vote.
Takes an indepth look at structures in C# and how their underlying mechanics work and what impacts they may have.

Introduction

I was recently very interested in taking a look at how structures in C# worked after having done a fair amount of previous work already with MSIL. During my investigation, I learned a few interesting things that I thought I would share here as most of them aren't blatantly obvious to someone who doesn't go looking.

Background

The majority of this article will use C# code (as well as IL) for examples, however, most of it, if not all of it, can be applied to VB.NET as well.

Structures cannot inherit but can implement interfaces, why?

The reason for this is very simple. The .NET infrastructure only allows classes to inherit one other type. This one other type is already occupied for structures by System.ValueType.

public struct Foo
{
    public int v;
    public int v2;
}

public class Foo2
{
    public int v;
    public int v2;
}

Results in the following:

.class public sequential ansi sealed beforefieldinit Foo
    extends [mscorlib]System.ValueType
{
    .field public int32 v
    .field public int32 v2
} 

.class public auto ansi beforefieldinit Foo2
    extends [mscorlib]System.Object
{
    .field public int32 v
    .field public int32 v2

    // parameterless constructor
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        .maxstack 8
        ldarg.0
        call instance void [mscorlib]System.Object::.ctor()
        ret
    }
}

The only notable differences (aside from the constructor) is that Foo is sealed meaning it cannot be inherited (which makes sense since classes cannot inherit structures, and structures cannot inherit anything). The only other notable difference is that the structure extends System.ValueType and the class extends System.Object. Had a type been specified to inherit from, System.Object would be that given type.

Casting structures to interfaces and vice-versa

One thing that may not be completely obvious to you is that when you cast a structure to an interface, this requires a box. And casting an interface to a structure requires an unbox. When you think about it, this makes perfect sense as a structure is a value type whereas an interface is a reference type.

** I didn't feel it was necessary to post code to justify this claim, if anyone feels otherwise please comment.

Why do all structures have a parameterless constructor?

The reason for this is the parameterless constructor isn't really a constructor at all. When you create a structure with no parameters (at least in C#, I'm not sure if any other .NET language allows you to implement parameterless constructors for structures as it's very possible to do at the IL level when you're the compiler), it actually generates the IL instruction initobj (which does not invoke a constructor).

What's interesting is that if you pass a structure via a pointer to a method, you're not required to initialize it anymore before using it. I suspect this is because the compiler treats passing it as a pointer similar to passing it as a parameter with the out modifier:

Foo foo;
DoStuff2(&foo);
// you can now freely use foo despite DoStuff2() being empty and doing nothing to pFoo

Using the ref/out keyword with structures

When you invoke a method passing a structure via ref/out, an address of that given structure is obtained and passed to the method -- but is this any different than passing the pointer yourself?

DoStuff(ref obj); 
DoStuff2(&obj);

Results in the following:

ldloca.s obj
call void Program::DoStuff(valuetype Foo&)

ldloca.s obj
conv.u
call void Program::DoStuff2(valuetype Foo*) 

Once you look up what conv.u is, it becomes apparent that there is no significant difference between obtaining the address of the variable or passing it by reference as they are exactly the same. The same applies to the out keyword. In reality, it's not really any different than the ref-keyword at the IL level other than the constraints it imposes when you're coding in C#.

There is however a difference between the pointer type and reference type. If the GC decides to move memory around, the reference's address will be updated as required and will have no impacts on the code what-so-ever, however, if the pointer Foo* points to gets moved, it now points to invalid memory which could cause nasty things to occur such as crashes.

Typically, you don't have to worry about this. Locals (stack declared variables) do not get moved by the GC (however, pointers to them become invalid after the method they exist in has exited) and if it is not a local, C# forces you to use the 'fixed' keyword to ensure the object is pinned down and does not get moved by the GC.

It is usually more inexpensive to pass a structure by reference via ref than it is to pass it by address using a pointer type due to the fact that passing it by a pointer type may require to pin the object down. In short, T& is safer than T*. This also explains why you can use T& (a ref-keyworded variable) as a parameter for a pointer type in a platform invoke call as you can safely convert from T& to T* implicitly.

Instance fields/methods of structures

When you invoke an instance method similar to above, the address of (reference to) the structure must first be obtained before the method is invoked as it is passed as the first argument. This explained why the this object of instance methods affect by reference the calling object. In constant, when a reference-type invokes an instance method, the reference-type is simply passed as the first argument and no address needs to be obtained as we already have a reference-type.

I suspect due to the this, instance methods of a structure (value type) are slower than an instance method of a class (reference type), but don't quote me on this as I haven't ran adequate tests to prove this, it just seems the most logical to me.

Unlike instance methods, when you set or get a value of a structure, a reference to the object is not obtained.

The fixed keyword

When a structure is bound to an object or the context of if it is or isn't unavailable (instance methods), you're required to use the fixed keyword to pin down an object and prevent it from being moved by the GC. Pinning the object down ensures that the pointer you obtain does not become invalid as it belongs to another object which could be moved by the GC.

Box<int> box = new Box<int>();
fixed (int* pInt = &box.Value)
{
    // logic...
}

The way this is achieved at the IL-level is through the pinned modifier:

.locals init (
   [0] class Box`1<int32> 'box',
   [1] int32& pinned pInt
)

// Box<int> box = new Box<int>();
    newobj instance void class Box`1<int32>::.ctor()
    stloc.0
    ldloc.0

// fixed (int* pInt = &box.Value)
        ldflda !0 class Box`1<int32>::Value
        stloc.1
// {
    nop
// }
        nop

// compiler added code
        ldc.i4.0
        conv.u
        stloc.1

        ret

While a pinned variable holds a reference, the reference will not be moved by the GC thereby preventing any owner object (in this case box) from being moved as well by the GC. At the end of the method, pInt is set to null (0) to allow the GC to move the object around again. This typically occurs at the end of a fixed scope. Due to the object being pinned down for the duration of the fixed statement, the object has a static address as it cannot be moved by the GC.

The default(T) operator

This operator does not exist at the IL-level. The operator is fairly straightforward. If T is a reference type, the default value returned is null. If T is a value type the return value is the equivalent of having used the new keyword which results in the instruction initobj.

Foo foo1 = new Foo();       // this is the same as default(Foo)
Foo foo2 = default(Foo);    // this is the same as new Foo();

The ? type modifier (Nullable types)

The symbol ? can be used to denote a reference type equivalent of a value type. This operator comes in handy when having to deal with boxing and unboxing as seen here. The reference equivalent can implicitly be casted to its value type representative. When the ? operator is used, it is actually syntactic sugar for Nullable<T>. Nullable<T> can be assigned to implicitly by its T-type as well as explicitly converted to its T-type. A Nullable<T> type is immutable which means once one is created, you cannot alter the value it holds (the Value property) as it is read-only. 

static void PrintInteger(int val)
{
    Console.WriteLine(val);
}

static void Main(string[ ] args)
{
    int? refInteger = 22;           // implicit assignment
    Nullable<int> refInteger2 = 55; // implicit assignment

    PrintInteger((int)refInteger2); // explicit cast
}

A well written overview of Nullable types and their uses can be found on MSDN.

License

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

Share

About the Author

roylawliet
Student
Canada Canada
I was born March 15th 1994 and I have been programming for about 5 years now and working with C# for about 3 years. I am experienced in a variety of programming languages such as C/C++, Delphi, VB.NET, F#, and Python. I absolutely love learning new things about computer science, so I'm always doing research and often reinventing the wheel to get a better grasp on concepts.

Comments and Discussions

 
Questionwhy the keyword of foo is used as example of class? Pinmemberadriancs4-Nov-12 5:59 
AnswerRe: why the keyword of foo is used as example of class? PinmemberHaBiX19-Nov-12 21:25 
AnswerRe: why the keyword of foo is used as example of class? PinmemberTheOtherCPian20-Nov-12 4:06 
AnswerRe: why the keyword of foo is used as example of class? Pinmemberadriancs21-Nov-12 0:19 
SuggestionNice Article [modified] PinmemberPavel Vladov30-Aug-12 22:47 
QuestionGood Stuff, have a 5 PinmemberDewey28-Aug-12 16:32 
QuestionMy vote of 5 PinmemberMizan Rahman20-Aug-12 21:54 
QuestionQuestion About Struct Inheritance PinprotectorAspDotNetDev20-Aug-12 13:26 
AnswerRe: Question About Struct Inheritance Pinmemberroylawliet20-Aug-12 13:43 
GeneralRe: Question About Struct Inheritance PinmemberDaniele Rota Nodari28-Jul-13 21:30 
QuestionVery Interesting Reading PinmvpDave Kerr20-Aug-12 9:05 
Questionnot bad PinmemberCIDev20-Aug-12 6:12 
AnswerRe: not bad Pinmemberroylawliet20-Aug-12 8:10 
GeneralRe: not bad PinmemberCIDev20-Aug-12 9:51 
General[My vote of 1] Missing Info about default PinmemberNicolas Penin19-Aug-12 21:35 
GeneralRe: [My vote of 1] Missing Info about default Pinmemberroylawliet20-Aug-12 7:41 
GeneralRe: [My vote of 1] Missing Info about default PinmvpDave Kerr20-Aug-12 9:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140814.1 | Last Updated 19 Nov 2012
Article Copyright 2012 by roylawliet
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid