20 Nov 2009

Value Types are a bit special. I won’t go down to the basics, but the differ from Reference Types by holding directly a value instead of a reference.

There are two interesting cases that I’m going to elaborate on. They pretty much make sense, but a little explanation cannot hurt.

Prelude

Here is a very simple struct that I will use to demonstrate my point:

struct DaysAndHours
{
    public int Days { get; private set; }
    public int Hours { get; private set; }

    public override string ToString()
    {
        return String.Format("{0} day(s) and {1} hour(s)", Days, Hours);
    }
}

Notes:

  • Structs cannot have custom default (parameter less) constructors. They however can have other custom constructors. This means that the default constructor can always be called, and that all the fields from the struct will be initialised at there default value;
  • Overriding the ToString method is a good idea. It’s always a good idea, even more with a struct as this will prevent boxing of the variable when ToString called.

The “unassigned” Value Type

Now, let’s say that I declare a variable of this type, and never assign it. The normal rules of the compiler will apply:

  • When you declare a variable somewhere, without initialising it, the compiler will issue a warning saying that the variable is declared but never used.
  • If you try to access one of it’s field, it will issue an error saying that the variable cannot be used because it is unassigned.

However, if you run the debugger, you can see the variable’s content. If it was a Reference Type, it would be null, but in this case, you can see all that the content is there, with all the fields being at their default value:

StructsInitialisation01

The trick is that, when declaring the variable dh, the instance is allocated on the stack. However, the C# compiler don’t let you do that and wants to make sure you called the new keyword for that value type.

The Backing Field  Issue

In my struct, I lazily used automatically implemented properties that were introduced in C# 3. This leads to a compiler error when using a custom constructor like this one:

public DaysAndHours(int days, int hours)
{
    Days = days;
    Hours = hours;
}

The compiler will report the following error:

Backing field for automatically implemented property 'DaysAndHours.Days' must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer.

This makes a lot of sense, as the backing fields for these properties have not been initialized. However, we cannot access the backing field, so we cannot initialize the field. Hopefully, the default parameter less constructor will do that, so calling it is sufficient to make sure the backing fields are initialized:

public DaysAndHours(int days, int hours) : this()
{
    Days = days;
    Hours = hours;
}

Conclusion

Always remember that you cannot prevent a struct to be constructed using the default constructor, that has the same visibility as the struct itself. If you are using automatic properties, you have to explicitly call the default constructor in order to initialise the backing fields as you can’t access them directly.



blog comments powered by Disqus