64

Kicking around some small structures while answering this post, I came across the following unexpectedly:

The following structure, using an int field is perfectly legal:

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Legal assignment.
    } 

    public int Size; 
}

However, the following structure, using an automatic property does not compile:

struct MyStruct
{ 
    public MyStruct ( int size ) 
    { 
        this.Size = size; // <-- Compile-Time Error!
    } 

    public int Size{get; set;}
}

The error returned is "The 'this' object cannot be used before all of its fields are assigned to". I know that this is standard procedure for a struct: the backing field for any property must be assigned directly (and not via the property's set accessor) from within the struct's constructor.

A solution is to use an explicit backing field:

struct MyStruct
{ 
    public MyStruct(int size)
    {
        _size = size;
    }

    private int _size;

    public int Size
    {
        get { return _size; }
        set { _size = value; }
    }
}

(Note that VB.NET would not have this issue, because in VB.NET all fields are automatically initialized to 0/null/false when first created.)

This would seem to be an unfortunate limitation when using automatic properties with structs in C#. Thinking conceptually, I was wondering if this wouldn't be a reasonable place for there to be an exception that allows the property set accessor to be called within a struct's constructor, at least for an automatic property?

This is a minor issue, almost an edge-case, but I was wondering what others thought about this...

Community
  • 1
  • 1
Mike Rosenblum
  • 12,027
  • 6
  • 48
  • 64
  • 2
    Fields in C# are initialized to 0/null/false too. Remember its the runtime that does it, not the specific language. ;) – David Anderson Feb 11 '09 at 06:00
  • Not for fields of structures in C#. For a struct, the fields must be initialized by the explicit constructor or by the caller if using the implicit, parameterless constructor. VB.NET does not have this limitation and, therefore, the example, above, that will not compile in C# will compile and run just fine in VB.NET. – Mike Rosenblum Mar 26 '11 at 02:08
  • 1
    possible duplicate of [Why is it necessary to call :this() on a struct to use automatic properties in c#?](http://stackoverflow.com/questions/272153/why-is-it-necessary-to-call-this-on-a-struct-to-use-automatic-properties-in-c) – nawfal Jun 03 '13 at 17:59

3 Answers3

81

From C# 6 onward: this is no longer a problem


Becore C# 6, you need to call the default constructor for this to work:

public MyStruct(int size) : this()
{
    Size = size;
}

A bigger problem here is that you have a mutable struct. This is never a good idea. I would make it:

public int Size { get; private set; }

Not technically immutable, but close enough.

With recent versions of C#, you can improve on this:

public int Size { get; }

This can now only be assigned in the constructor.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc, this makes perfect sense, thank you, I forgot that we can force the default initialization. And I agree, I would never make a struct mutable, but I was replying to someone else's answer here. – Mike Rosenblum Jan 07 '09 at 14:30
  • 5
    I think saying *never* is a bit strong. One should default to immutable, but making it sound like such a mandate "is never a good idea." ;) – Erich Mirabal May 14 '09 at 14:44
  • 9
    Using absolutes is **never** a good idea ;-p (see what I did there...). Indeed, *if you understand the implications*, mutable structs are usable. The problem is that most people don't understand them, and it costs them lots of time and frustration wile they learn. – Marc Gravell May 14 '09 at 15:07
  • 1
    @MarcGravell, why is having a mutable structure bad? Shouldn't treat my structure like any other POD (ex: integer, double, float)? – dotnetN00b Feb 17 '12 at 19:39
  • 7
    @dotnetN00b yes, you should! Hint: integers, doubles and floats are all immutable :p there is no issue re-assigning the entire value (except maybe atomic it's in a threading scenario); the problem is when you edit **parts** of a single value in-situ, which often leads to lost edits, unless you are very careful. – Marc Gravell Feb 17 '12 at 19:51
  • The technique where you chain `:this()` is arguable the best and most elegant one. But you can write it in other ways, for example `public MyStruct(int size) { this = new MyStruct { Size = size, }; }`. Of course the `new MyStruct` with no constructor arguments is really the same as `:this()`. – Jeppe Stig Nielsen Nov 18 '13 at 11:02
  • @MarcGravell By leaving out the `private set;` altogether you create a true readonly backing field and thereby a immutable struct. – riezebosch Jul 06 '17 at 07:07
  • @riezebosch it does now; it didn't when I wrote this answer :) although actually IIRC it doesn't mark the *field* readonly at the IL level; the upcoming "readonly ref/struct" stuff should improve that in vFuture – Marc Gravell Jul 06 '17 at 07:17
  • @MarcGravell I've tested it with VS2017 and it does! For .NET Core and .NET Full both get a generated `initonly` backing field. – riezebosch Jul 06 '17 at 10:57
  • @riezebosch fair enough - I've checked, and indeed: `initonly` – Marc Gravell Jul 06 '17 at 11:17
  • @riezebosch: Structs don't have control over whether they are mutable. You can create a struct which can only be mutated by copying another struct onto it, but all structs are mutable if they are stored in something that allows them to be mutated, and immutable if they are stored in something that does not allow them to be. – supercat Jul 06 '17 at 15:11
  • @supercat I'm not sure what you're saying. When you use `public int MyProperty { get; }` you get a backing _private_ backing field that is marked _initonly_ so it can only be assigned from the constructor. How's that mutable to you? – riezebosch Jul 17 '17 at 07:28
  • @riezebosch: Using the assignment operator to copy a value of structure type to a variable or field of that same type will mutate the destination by copying the values of all the fields from the source. – supercat Jul 17 '17 at 15:53
  • @supercat I think most people would say that it was the *parent* that got mutated in that case, not the struct. By your definition, integers would count as mutable, which: they aren't (at least, not in any meaningful sense) – Marc Gravell Jul 17 '17 at 16:19
  • @MarcGravell: All of the methods that are defined on `Int32` behave as though invoked by value, but some structure methods don't. If a `KeyValuePair` gets changed while its `ToString()` method is running, for example, its return value may reflect the old value of `T` and the new value of `U`. Such a thing should not be possible with an immutable type. – supercat Jul 17 '17 at 17:04
  • @supercat you're talking about a torn read - or something very similar involving multiple accesses to fields on a value - that's a completely different concept to mutability, and is again down to the parent. – Marc Gravell Jul 17 '17 at 17:07
  • @MarcGravell: Not necessarily a torn read, though a proper immutable type wouldn't have an issue with torn reads. `KeyValuePair` will call `ToString` on `Key` and then `Value`. If the `Key.ToString()` call overwrites the field in which the `KeyValuePair` had been stored, the `Value.ToSTring()` call will reflect that change. No need for multi-threading. – supercat Jul 17 '17 at 17:15
  • @supercat yes it would; anything over the CPU fetch width is liable to that behavior in race conditions. You can get that issue for `long` on x86, for example. It comes down to doing a single atomic read for all access. It has little to do with whether the value itself is immutable, but rather: whether the **memory** is ever changed to a different value, which comes down to: is the parent and every parent above that also immutable. But: the value itself is still immutable (for any useful definition of immutable). – Marc Gravell Jul 17 '17 at 17:43
  • @MarcGravell: If `classField` is of class type, invoking `classField.someMethod()` will make a snapshot of that field's value (i.e. the reference stored therein) available to the called method as `this`; the value of `this` will not change during that method. If `structField` is of struct type, invoking `structField.someMethod` will pass a *live view* of the underlying storage; the value of `this` may be changed during execution by things the method knows nothing about. – supercat Jul 17 '17 at 19:07
  • 1
    @supercat the joys of `this` for `struct` being a `ref SomeType`.... but again, I *understand the problem*: I just disagree that this means that `SomeType` is "mutable". Rather, *memory* is mutable - but memory is always mutable. – Marc Gravell Jul 17 '17 at 19:10
11

You can fix this by first calling the default constructor:

struct MyStruct 
{
    public MyStruct(int size) : this() 
    {
        this.Size = size; // <-- now works
    }

     public int Size { get; set; }
}
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Stormenet
  • 25,926
  • 9
  • 53
  • 65
8

Another obscure work-around to this problem is one spotted in the temporary Tuple class in the Managed Extensibility Framework (via Krzysztof Koźmic):

public struct TempTuple<TFirst, TSecond>
{
    public TempTuple(TFirst first, TSecond second)
    {
        this = new TempTuple<TFirst, TSecond>(); // Kung fu!
        this.First = first;
        this.Second = second;
    }

    public TFirst First { get; private set; }
    public TSecond Second { get; private set; }

(Full source code from Codeplex: Tuple.cs)

I also note that the documentation for CS0188 has been updated to add:

If you see this error when trying to initialize a property in a struct constructor, the solution is to change the constructor parameter to specify the backing field instead of the property itself. Auto-implemented properties should be avoided in structs because they have no backing field and therefore cannot be initialized in any way from the constructor.

So I take that to mean that the official guidance is to use old-style properties in your structs when you run in to this problem, which is probably less obscure (and more readible) than either of the other two alternatives explored so far.

Daniel Fortunov
  • 43,309
  • 26
  • 81
  • 106
  • Thanks for the update on the error report. Indeed, the most efficient situation for structs is to use an explicit backing field. If one is looking to initialize to something other than 0/null/false, then this requires *two* steps if there is no backing field: null out and then set the actual value. – Mike Rosenblum Feb 11 '09 at 14:51
  • 3
    The "knung fu" line is really cool, I must say. (Couldn't be done with a class.) But this is the same as having the constructor being declared 'public TempTuple(TFirst first, TSecond second):this()', which, I think, is a cleaner way to do this. (And we still wind up initializing each field *twice*.) – Mike Rosenblum Feb 11 '09 at 14:56