Why Must I Initialize All Fields in my C# struct w

2019-01-17 22:41发布

问题:

I would like to try this code:

public struct Direction
{
   private int _azimuth;

   public int Azimuth
   {
     get { return _azimuth; }
     set { _azimuth = value; }
   }       

   public Direction(int azimuth)
   { 
      Azimuth = azimuth
   } 
}

But it fails on compilation, I understand that struct need to init all its fields. but i am trying to understand what happens under the CLR\IL hoods. why it need all the fields before any other method\property\this etc.

Thanks.

回答1:

Value Types are created on the stack (unless nested within a reference type) There is something about fields/locations on the stack that the CLR can't guarantee that they will be zeroed out (contrary to fields/locations on the managed heap which are guaranteed to be zeroed out). Hence they must be written to before being read. Otherwise it's a security hole.

The struct's default ctor (which takes no parameters and which you're not allowed to explicitly specify) zeroes out all fields of a struct and hence you can use a struct after you do.

new BimonthlyPairStruct()

However when you implement your parameterized ctor, you must ensure that all fields have been initialized - which is required for the CLR to pass your code as safe/verified .

See Also: CLR via C# 2nd Ed - Pg 188



回答2:

This is because structs are derived from System.ValueType and not System.Object, System.ValueType implements default constructur which you cannnot override, this default constructer initializes all fields in struct with its default value. So if you are implementing any parameter contructor in your class you will also need t0 ensure you invoke system.ValueType default const. And to answer why it needs to init all its value this is because value are stored in stack memory.



回答3:

This works:

  public Direction(int azimuth)
  {
    _azimuth = azimuth;
  }

From the spec:

Struct constructors are invoked with the new operator, but that does not imply that memory is being allocated. Instead of dynamically allocating an object and returning a reference to it, a struct constructor simply returns the struct value itself (typically in a temporary location on the stack), and this value is then copied as necessary.

Basically, the compiler must see that every field gets initialized in the constructor so that it can copy those values, and it is not willing to examine calls to functions or properties.



回答4:

I just found an explanation in the MSDN forum stating that this rule is enforced because zeroing out the memory is skipped if you use a none default constructor. So you will have to provide initialization values for all fields in order to avoid some fields containing random values. You achieve this easily be calling the parameter less default constructor, but at the cost of initializing some fields twice.

I cannot tell if this explanation is correct, but it sounds reasonable.

When you define a non-default initializer, C# requires you to set all fields because it skips the zeroing of memory and lets you initialize it - otherwise you'd have to have a double initialization performance hit. If you don't care about the (very slight) performance hit you can always chain a call to the : this() initializer and then only initialize selected fields.



回答5:

public struct Direction
{
    public int Azimuth { get; private set; }

    public Direction(int azimuth) : this()
    {
        Azimuth = azimuth;
    }
}


回答6:

You need to initialize the field, and not via the property.