Почему я должен инициализировать все поля в моей структуре C# с помощью конструктора не по умолчанию?

Я хотел бы попробовать этот код:

public struct Direction
{
   private int _azimuth;

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

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

Но при компиляции это не удается, я понимаю, что структура должна инициализировать все свои поля. но я пытаюсь понять, что происходит под капотом CLR\IL. зачем ему нужны все поля перед любым другим методом\свойством\этим и т. д.

Спасибо.


person rabashani    schedule 06.04.2009    source источник


Ответы (6)


Типы значений создаются в стеке (если они не вложены в ссылочный тип). В полях/местоположениях в стеке есть что-то такое, что CLR не может гарантировать, что они будут обнулены (в отличие от полей/местоположений в управляемой куче, которые гарантированно обнуляется). Следовательно, они должны быть записаны до того, как будут прочитаны. В противном случае это дыра в безопасности.

Ctor структуры по умолчанию (который не принимает параметров и который вам не разрешено указывать явно) обнуляет все поля структуры, и, следовательно, вы можете использовать структуру после того, как вы это сделаете.

new BimonthlyPairStruct()

Однако, когда вы реализуете свой параметризованный ctor, вы должны убедиться, что все поля были инициализированы — это необходимо для того, чтобы CLR передала ваш код как безопасный/проверенный .

См. также: CLR через C#, 2-е изд., стр. 188.

person Gishu    schedule 25.04.2009
comment
Допустимо передавать еще неинициализированные переменную или поле в качестве параметра out внешнему или виртуальному методу, даже если компилятор должен убедиться, что он инициализирован перед этим (другой метод может быть написан на языке, который позволяет out параметры, которые необходимо прочитать перед записью). Поскольку компилятор должен вставить код инициализации в этом сценарии, он почти наверняка должен быть в состоянии сделать это в любом сценарии, если бы его разработчики были к этому склонны. - person supercat; 06.12.2012
comment
Любопытно, почему компилятор C# не вставляет неявно : this() в таких случаях? - person Vlad; 13.02.2017
comment
@Vlad Потому что поля потенциально могут быть инициализированы дважды. Один раз вызовом базового конструктора и другой раз конструктором пользователя (если пользователь должен был инициализировать поле). У Даниэля Брюкнера есть ответ, который объясняет это. - person Alex Jorgenson; 06.08.2018

Это связано с тем, что структуры являются производными от System.ValueType, а не System.Object, System.ValueType реализует конструктор по умолчанию, который вы не можете переопределить, этот конструктор по умолчанию инициализирует все поля в структуре его значением по умолчанию. Поэтому, если вы реализуете какой-либо конструктор параметров в своем классе, вам также потребуется t0, чтобы убедиться, что вы вызываете константу по умолчанию system.ValueType. И чтобы ответить, почему ему нужно инициализировать все свое значение, это потому, что значение хранится в памяти стека.

person Brijesh Mishra    schedule 06.04.2009

Это работает:

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

Из спецификации:

Конструкторы структур вызываются с помощью оператора new, но это не означает, что выделяется память. Вместо того, чтобы динамически выделять объект и возвращать ссылку на него, конструктор структуры просто возвращает само значение структуры (обычно во временном месте в стеке), и это значение затем копируется по мере необходимости.

По сути, компилятор должен видеть, что каждое поле инициализируется в конструкторе, чтобы он мог копировать эти значения, и он не хочет проверять вызовы функций или свойств.

person RossFabricant    schedule 06.04.2009

Я только что нашел объяснение в Форум MSDN утверждает, что это правило применяется, потому что обнуление памяти пропускается, если вы используете конструктор без значения по умолчанию. Таким образом, вам придется указать значения инициализации для всех полей, чтобы избежать некоторых полей, содержащих случайные значения. Вы легко добьетесь этого, вызвав конструктор по умолчанию без параметра, но за счет двойной инициализации некоторых полей.

Я не могу сказать, верно ли это объяснение, но оно звучит разумно.

Когда вы определяете инициализатор не по умолчанию, C# требует, чтобы вы установили все поля, потому что он пропускает обнуление памяти и позволяет вам ее инициализировать - в противном случае вам пришлось бы иметь двойной удар по производительности инициализации. Если вас не волнует (очень небольшое) снижение производительности, вы всегда можете связать вызов инициализатора : this() , а затем инициализировать только выбранные поля.

person Daniel Brückner    schedule 06.04.2009
comment
Интересно отметить, что в то время как выражения формы structVar = structExpr обычно перезаписывают все поля первой структуры соответствующими полями последней, это не относится к structVar = new StructType(params); Выражения этой формы иногда делают временное, передают его конструктору , а затем скопируйте его в указанную переменную, но иногда они просто вызывают конструктор, при этом затронутая переменная фактически передается как параметр ref (который не обнуляется первым). - person supercat; 07.12.2012

Вам нужно инициализировать поле, а не через свойство.

person leppie    schedule 06.04.2009
comment
Эй, я не ищу Как правильно написать - я ищу объяснение почему... - person rabashani; 06.04.2009

person    schedule
comment
Эй, я не ищу Как правильно написать - я ищу объяснение почему... - person rabashani; 06.04.2009