Question

If I define a struct in C# using automatic properties like this:

public struct Address
{
    public Address(string line1, string line2, string city, string state, string zip)
    {
        Line1 = line1;
        Line2 = line2;
        City = city;
        State = state;
        Zip = zip;
    }

    public string Line1 { get; protected set; }
    public string Line2 { get; protected set; }
    public string City { get; protected set; }
    public string State { get; protected set; }
    public string Zip { get; protected set; }
}

When I attempt to build the file, I get a compilation error saying The 'this' object cannot be used before all of its fields are assigned to. This can be solved by changing the constructor to make a chained call to the default constructor like this:

public Address(string line1, string line2, string city, string state, string zip): this()
{
    Line1 = line1;
    Line2 = line2;
    City = city;
    State = state;
    Zip = zip;
}

My question is, why does this work, and what is happening? I have a guess, and I tried to prove it by looking at IL, but I'm only kidding myself if I think I can break down IL. But my guess is, auto properties work by having the compiler generate fields for your properties behind the scenes. Those fields cannot be accessed through code, all setting and getting must be done through the properties. When creating a struct, a default constructor cannot be explicitly defined. So behind the scenes, the compiler must be generating a default constructor that sets the values of the fields that the developer can't see.

Any and all IL wizards are welcome to prove or disprove my theory.

Was it helpful?

Solution

Note: as of C# 6, this isn't required - but you should be using read-only automatically-implemented properties with C# 6 anyway...

this() makes sure that the fields are definitely assigned as far as the compiler is concerned - it sets all fields to their default values. You have to have a fully constructed struct before you can start accessing any properties.

It's annoying, but that's the way it is. Are you sure you really want this to be a struct though? And why use a protected setter on a struct (which can't be derived from)?

OTHER TIPS

A property is nothing more than an encapsulation of a Get method and/or a Set method. The CLR has metadata which indicates that particular methods should be regarded as being a properties, meaning compilers should allow some constructs which it would not allow with methods. For example, if X is a read-write property of Foo, a compiler will translate Foo.X += 5 into Foo.SET_X_METHOD(Foo.GET_X_METHOD() + 5) (though the methods are named differently, and are not generally accessible by name).

Although an autoproperty implements a pair of get/set methods which access a private field in such a way as to behave more or less like a field, from the point of view of any code outside the property, an autoproperty is a pair of get/set methods just like any other property. Consequently, a statement like Foo.X = 5; is translated as Foo.SET_X_METHOD(5). Since the C# compiler just sees that as a method call, and since methods do not include any metadata to indicate what fields they read or write, the compiler will forbid the method call unless it knows every field of Foo has been written.

Personally, my advice would be to avoid the use of autoproperties with structure types. Autoproperties make sense with classes, since it's possible for class properties to support features like update notifications. Even if early versions of a class do not support update notifications, having those versions use an autoproperty rather than a field will mean that future versions can add update-notification features without requiring consumers of the class to be reworked. Structures, however, cannot meaningfully support most of the types of features that one might wish to add to field-like properties.

Further, the performance differences between fields and properties is much greater with large structures than it is with class types. Indeed, much of the recommendation to avoid large structures is a consequence of this difference. Large structures can actually be very efficient, if one avoids copying them unnecessarily. Even if one had an enormous structure HexDecet<HexDecet<HexDecet<Integer>>>, where HexDecet<T> contained exposed fields F0..F15 of type T, a statement like Foo = MyThing.F3.F6.F9; would simply require reading one integer from MyThing and storing it to Foo, even though MyThing would by huge by struct standards (4096 integers occupying 16K). Additionally, one could update that element very easily, e.g. MyThing.F3.F6.F9 += 26;. By contrast, if F0..F15 were auto-properties, the statement Foo = MyThing.F3.F6.F9 would require copying 1K of data from MyThing.F3 to a temporary (call it temp1, then 64 bytes of data from temp1.F6 to temp2) before finally getting around to reading 4 bytes of data from temp2.F9. Ick. Worse, trying to add 26 to the value in MyThing.F3.F6.F9 would require something like var t1 = MyThing.F3; var t2 = t1.F6; t2.F9 += 26; t1.F6 = f2; MyThing.F3 = t1;.

Many of the long-standing complaints about "mutable structure types" are really complaints about structure types with read/write properties. Simply replace properties with fields and the problems go away.

PS: There are times it can be useful to have a structure whose properties access a class object to which it holds a reference. For example, it would be nice to have a version of an ArraySegment<T> class which allowed one to say Var foo[] = new int[100]; Var MyArrSeg = New ArraySegment<int>(foo, 25, 25); MyArrSeg[6] += 9;, and have the last statement add nine to element (25+6) of foo. In older versions of C# one could do that. Unfortunately, the frequent use of autoproperties in the Framework where fields would have been more appropriate led to widespread complaints about the compiler allowing property setters to be called uselessly on read-only structures; consequently, calling any property setter on a read-only structure is now forbidden, whether or not the property setter would actually modify any fields of the struct. If people had simply refrained from making structs mutable via property setters (making fields directly accessible when mutability was appropriate) compilers would never have had to implement that restriction.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top