Compiler error of "Non-nullable field is uninitialized" even though it was initialized in InitializeComponents function
Asked Answered
T

3

11

In WinForms it is common that a common initialization function is initializing reference variables (for example)

class SomeClass : Form {
  Button b;

  SomeClass() {
    InitializeComponents();
  }

  SomeClass(Container x) {
    InitializeComponents();
  }

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

As you can see b is always initialized to a non-null value. However, C# 8 will still complain that SomeClass() does not initialize non-nullable value b.

Of course I could mark b as nullable (Button? b) however, now I will get a warning on every usage of b, since nullability is not checked (it cannot be null...)

What is the best way to resolve this. Is there an attribute that can be used to flag InitializeComponent as being always called by constructor?

Please note, this is a very common pattern in WinForms (every component...)

Yuval

Thorrlow answered 25/3, 2019 at 12:32 Comment(0)
C
9

As per the preview docs:

Q: Why are warnings reported for fields that are initialized indirectly by the constructor, or outside the constructor?

A: The compiler recognizes fields assigned explicitly in the current constructor only, and warns for other fields declared as non-nullable. That ignores other ways fields may be initialized such as factory methods, helper methods, property setters, and object initializers. We will investigate recognizing common initialization patterns to avoid unnecessary warnings.

So, there is no way, right now to achieve what you want without moving that assignment directly into the constructor (or assigning it on the line that declares it).

Cons answered 25/3, 2019 at 12:43 Comment(2)
Since you've answered this, some attributes have been added like [MaybeNull]. Do you know if any has been added that tells the compiler the field is always initialized?Flexuous
I am not aware of such a thing @andre_ss6.Cons
J
5

For your very specific example the solution is to merge InitializeComponent() into the default constructor and to invoke it from the second one.

class SomeClass : Form {
  private readonly Button b;

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

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

Unfortunately this is a well-known current limitation and, moreover, all the designer generated code won't follow this pattern then you may need to put some #nullable disable (or one of the other directives, as appropriate) here and there.

Juggler answered 25/3, 2019 at 12:52 Comment(0)
T
0

MemberNotNull attribute should help in those situations.

In case from question it should work too:

    class SomeClass : Form {
      Button b;
    
      SomeClass() {
        InitializeComponents();
      }
    
      [MemberNotNull(nameof(b))]
      void InitializeComponents() {
        b = new Button();
      }
    }
These answered 24/3 at 15:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.