Ошибка компилятора «Ненулевое поле неинициализировано», даже если оно было инициализировано в функции InitializeComponents

#c# #winforms #c#-8.0 #nullable-reference-types

#c# #winforms #c #-8.0 #nullable-reference-types

Вопрос:

В WinForms обычно обычная функция инициализации инициализирует ссылочные переменные (например)

 class SomeClass : Form {
  Button b;

  SomeClass() {
    InitializeComponents();
  }

  SomeClass(Container x) {
    InitializeComponents();
  }

  void InitializeComponents() {
    b = new Button();
  }
}
  

Как вы можете видеть, b всегда инициализируется ненулевым значением. Однако C # 8 по-прежнему будет жаловаться на то, что SomeClass() не инициализирует ненулевое значение b.

Конечно, я мог бы пометить b как обнуляемое (кнопка? b) однако теперь я буду получать предупреждение при каждом использовании b, поскольку возможность обнуления не проверяется (оно не может быть нулевым …)

Каков наилучший способ решить эту проблему. Существует ли атрибут, который можно использовать для пометки InitializeComponent как всегда вызываемый конструктором?

Пожалуйста, обратите внимание, это очень распространенный шаблон в WinForms (каждый компонент …)

Yuval

Ответ №1:

Согласно документам предварительного просмотра:

Вопрос: Почему выдаются предупреждения для полей, которые инициализируются косвенно конструктором или вне конструктора?

A: Компилятор распознает поля, назначенные явно только в текущем конструкторе, и предупреждает о других полях, объявленных как ненулевые. Это игнорирует другие способы инициализации полей, такие как фабричные методы, вспомогательные методы, установщики свойств и инициализаторы объектов. Мы рассмотрим распознавание распространенных шаблонов инициализации, чтобы избежать ненужных предупреждений.

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

Комментарии:

1. Поскольку вы ответили на это, были добавлены некоторые атрибуты, подобные [MaybeNull] . Вы знаете, было ли добавлено что-либо, сообщающее компилятору, что поле всегда инициализировано?

2. Я не знаю о такой вещи @andre_ss6.

Ответ №2:

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

 class SomeClass : Form {
  private readonly Button b;

  public SomeClass() {
    b = new Button();
  }

  public SomeClass(Container x): this() {
    // Something else...
  }
}
  

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