5

I have:

public abstract class Craft
{
    public Craft()
    {
        CurrentQuadrant = new Point(0, 0);
    }

    private Point _currentQuadrant;
    public Point CurrentQuadrant
    {
        get => _currentQuadrant;
        set
        {
            _currentQuadrant = value;
        }
    }
}

(Point is a simple x-y pair).

Why would that be giving me the warning that Non-nullable field '_currentQuadrant' must contain a non-null value when exiting the constructor? Doesn't the assignment make sure it's non-null?

digitig
  • 1,989
  • 3
  • 25
  • 45
  • 2
    I don't think the linked answer actually helps you. The problem is that the compiler isn't smart enough to see that you assign the `CurrentQuadrant` property, and that in turn always assigns the `_currentQuadrant` field. Either set `_currentQuadrant` directly from the constructor, or make `CurrentQuadrant` an auto-property without an explicit backing field, i.e. `public Point CurrentQuadrant { get; set; }` – canton7 Nov 26 '21 at 14:27
  • 1
    `_currentQuadrant = new Point(0, 0);` – Caius Jard Nov 26 '21 at 14:27
  • @Wyck Nullable Reference Types came out with C# 8, 2 major versions ago – canton7 Nov 26 '21 at 14:30
  • *I'm sure I'm..* - if you've reached a point where youre certain youre being more clever than the compiler is capable of giving you credit for, warning suppression applies – Caius Jard Nov 26 '21 at 14:30
  • The compiler is warning that we should assign `_currentQuadrant` a value before exiting the constructor. It appears that we did, albeit indirectly, by calling the property setter for `CurrentQuadrant`. Is there maybe some way an exception could occur that would leave `_currentQuadrant` without a value? This question needs an answer that is not provided in the duplicate. e.g.: like canton7's first comment, which hints that the actual reason is that the compiler isn't smart enough (yet) to conclude that an assignment happened in this case. – Wyck Nov 26 '21 at 14:58
  • As Caius said (`_currentQuadrant = new Point(0, 0);`), plus if you do that you can remove the default constructor entirely. – Matthew Watson Nov 26 '21 at 16:18
  • Thanks, all. I needed reassuring that I hadn't missed a way it could end up null. Oh, and there's a reason I need it backed by a member and can't use a default constructor, but I'd simplified it away to create a minimum example for my question. – digitig Nov 26 '21 at 19:40
  • One very good reason to leave the constructor as-is (and suppress the warning) is if one wants to add some validation logic for the `Point` that should be shared between the constructor and the setter. Of course, there are other ways around the problem, but the straightforwardness of handing validation in the setter is compelling. – Leo Orientis Jun 02 '22 at 12:04

1 Answers1

8

Too little reputation to comment, so I'll post it here.

If you're using .NET 5+, you can use the [MemberNotNull] attribute (more info here).

Just put [MemberNotNull(nameof(_currentQuadrant)] on your constructor and the warning will disappear.

Or you could just assign the value to _currentQuadrant instead of CurrentQuadrant.

K. L.
  • 231
  • 2
  • 12
  • I put the `[MemberNotNull(nameof(_currentQuadrant)]` attribute at the top of the `CurrentQuadrant` setter instead of the constructor and that did the trick. – gunnerone Jul 12 '23 at 20:56