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

Asked
Viewd6852

12

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

 public struct Direction
{
   private int _azimuth;

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

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

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

Спасибо.

6 ответов

11

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

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

 new BimonthlyPairStruct()
 

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

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

5

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

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

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

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

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

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

    public Direction(int azimuth) : this()
    {
        Azimuth = azimuth;
    }
}
 
  • Эй, я не ищу «Как правильно писать» - Я ищу объяснение, почему…

    rabashani06 апреля 2009, 12:50
-1

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

  • Эй, я не ищу «Как правильно писать» - Я ищу объяснение, почему…

    rabashani06 апреля 2009, 12:51
5

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

5

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

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

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