Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compilation error. Using properties with struct

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.
like image 640
Captain Comic Avatar asked Dec 02 '10 14:12

Captain Comic


2 Answers

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.

like image 73
Ani Avatar answered Nov 03 '22 06:11

Ani


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:

  1. Implements automatic properties.
  2. Works out which members are dealt with relative to this.
  3. Gives all structs 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.

like image 3
Jon Hanna Avatar answered Nov 03 '22 07:11

Jon Hanna