Please explain the following error on struct constructor. If i change struct to class
the erros are gone.
public struct DealImportRequest
{
public DealRequestBase DealReq { get; set; }
public int ImportRetryCounter { get; set; }
public DealImportRequest(DealRequestBase drb)
{
DealReq = drb;
ImportRetryCounter = 0;
}
}
- error CS0188: The 'this' object cannot be used before all of its fields are assigned to
- error CS0843: Backing field for automatically implemented property
'DealImportRequest.DealReq' must be fully assigned before control is returned to the caller. Consider calling the default constructor from a constructor initializer.
As the error message recommends, you can resolve this by calling the default constructor from a constructor initializer.
public DealImportRequest(DealRequestBase drb) : this()
{
DealReq = drb;
ImportRetryCounter = 0;
}
From the language specification:
10.7.3 Automatically implemented properties
When a property is
specified as an automatically
implemented property, a hidden backing
field is automatically available for
the property, and the accessors are
implemented to read from and write to
that backing field. [...] Because the
backing field is inaccessible, it can
be read and written only through the
property accessors, even within the
containing type. [...] This
restriction also means that definite
assignment of struct types with
auto-implemented properties can only
be achieved using the standard
constructor of the struct, since
assigning to the property itself
requires the struct to be definitely
assigned. This means that user-defined
constructors must call the default
constructor.
The other (more verbose) alternative, of course, is to manually implement the properties and set the backing fields yourself in the constructor.
Do note that the struct you have there is mutable. This is not recommended. I suggest you either make the type a class (your compilation problems should go away immediately) or make the type immutable. The easiest way to accomplish this, assuming the code you have presented is the entire struct, would be to make the setters private (get; private set;
). Of course, you should also make sure that you don't add any mutating methods to the struct afterwards that rely on private access to modify the fields. Alternatively, you could back the properties with readonly
backing fields and get rid of the setters altogether.
The code you have is equivalent to the following code:
public struct DealImportRequest
{
private DealRequestBase _dr;
private int _irc;
public DealRequestBase DealReq
{
get { return _dr; }
set { _dr = value; }
}
public int ImportRetryCounter
{
get { return _irc; }
set { _irc = value; }
}
/* Note we aren't allowed to do this explicitly - this is didactic code only and isn't allowed for real*/
public DealImportRequest()
{
this._dr = default(DealRequestBase); // i.e. null or default depending on whether this is reference or value type.
this._irc = default(int); // i.e. 0
}
public DealImportRequest(DealRequestBase drb)
{
this.DealReq = drb;
this.ImportRetryCounter = 0;
}
}
Now, all I have done here is remove the syntactic sugar that:
- Implements automatic properties.
- Works out which members are dealt with relative to
this
.
- Gives all
struct
s a default no-parameter constructor.
The first two are optional (you could write them explicitly if you wished) but the third is not - we aren't allowed to write our own code for a struct
's parameterless constructor, we have to go with one that works like the one in the code above being given to us automatically.
Now, looked at here, suddenly the meaning of the two errors becomes clear - your constructor is implicitly using this
before it's fields are assigned (error 188) and those fields are those backing the automatic properties (error 843).
It's a combination of different automatic features that normally we don't have to think about, but in this case don't work well. We can fix this by following the advice in the error message for 843 and calling the default constructor as part of your explicit constructor:
public DealImportRequest(DealRequestBase drb)
:this()
{
DealReq = drb;
ImportRetryCounter = 0;
}
Considering this in relation to my expanded version of your code above, you can see how this solves the problem, because it calls the constructor that assigns to the backing fields before it proceeds.
I would recommend not using auto-properties with structures unless you have a good reason to use them. Wrapping a class field in a read-write property is useful because it makes it possible for an instance to control the circumstances where it may be read or written, and take action when a read or write takes place. Further, code within an object instance can identify the instance being acted upon, and may thus perform a special action only when reading and writing a particular instance. Using an auto-property in an early version of a class will make it possible for future versions of the class to use a manually-implemented property including the aforementioned benefits while retaining compatibility with already-compiled client code. Unfortunately, wrapping a struct field in a read-write property doesn't offer those same benefits because the fields of one struct instance can be copied to another without either instance having any say in the matter. If the semantics of a struct allow a property to be written with arbitrary values in most instances [as would be the case for an auto-property], then any legitimate replacement would be semantically equivalent to a field.