Nullable reference types and constructor warnings
Asked Answered
B

4

15

I'm trying to embrace C# 8's nullable references types in my project and make it smoothly work with EF Core.

Following this guide, I made my entity classes have constructors accepting all data needed for initializing their non-nullable properties:

public class MyEntity
    {
        public MyEntity(int someNumber, string name, string shortName, bool active)
        {
            SomeNumber= someNumber;
            Name = name;
            ShortName = shortName;
            Active = active;
        }

        public int SomeNumber { get; set; }
        public string Name { get; set; }
        public string ShortName { get; set; }
        public string? SomethingOptional { get; set; }
        public bool Active { get; set; }
    }

In my business case I sometimes need to update all properties of the entity. I can use property setters, but since I want to make sure I don't omit anything by doubling the initialization syntax (and in reality my entities can have 10 or more properties), I decided to create a public Update() function for convenience and call it instead of constructor body:

        public MyEntity(int someId, string name, string shortName, bool active)
        {
            Update(someId, name, shortName, active);
        }

        public void Update(int someId, string name, string shortName, bool active)
        {
            SomeNumber = someId;
            Name = name;
            ShortName = shortName;
            Active = active;
        }

Now, when creating the entity, I call the constructor, and when changing it, I call Update(). However, now the compiler gives nullability warning (CS8618) that the non-nullable properties are not initialized by the constructor. It's apparently unable to guess that calling Update will initialize them.

I've maybe overengineered it a bit with this Update() method, but now I'm curious is there a way to make the compiler trust that my constructor will initialize the properties?

Badminton answered 15/4, 2020 at 6:48 Comment(0)
C
26

It is indeed a bit verbose, but you can use the [MemberNotNull] attribute in .NET 5 to tell the compiler that a method ensures a member is not null after it returns.

using System.Diagnostics.CodeAnalysis;
    
public class MyEntity
{
    [MemberNotNull(nameof(Name), nameof(ShortName))]
    public void Update(int someId, string name, string shortName, bool active)
    {
        SomeNumber = someId;
        Name = name;
        ShortName = shortName;
        Active = active;
    }
}
Cambium answered 22/11, 2020 at 19:9 Comment(0)
A
0

If you are sure what you are doing, then you can suppress compiler warning by

#pragma warning disable CS8618
public MyEntity(int someNumber, string name, string shortName, bool active)
{
    SomeNumber= someNumber;
    Name = name;
    ShortName = shortName;
    Active = active;
}
#pragma warning disable CS8618

also there is an open case for that in github, you can check it. You can use the approach below (quoted from github)

class MyClass
{
    private string m_str1;
    private string m_str2;

    public MyClass()
    {
        Init(out m_str1, out m_str2);
    }

    private void Init(out string str1, out string str2)
    {
        str1 = string.Empty;
        str2 = string.Empty;
    }
}
Armourer answered 15/4, 2020 at 7:2 Comment(1)
I think you intended for the second #pragma instruction to be #pragma warning restore CS8618Unceremonious
E
0

I'm just learning and faced the same problem, and checked answers by other but isn't there a much more simpler solution - just to assign an initial value upon declaration?

public class MyEntity
    {
        public MyEntity(int someId, string name, string shortName, bool active)
        {
            Update(someId, name, shortName, active);
        }

        public void Update(int someId, string name, string shortName, bool active)
        {
            SomeNumber = someId;
            Name = name;
            ShortName = shortName;
            Active = active;
        }

        public int SomeNumber { get; set; }
        // initialize an empty string to get rid of the warning
        public string Name { get; set; } = string.Empty;
        // initialize an empty string to get rid of the warning
        public string ShortName { get; set; } = string.Empty;
        public string? SomethingOptional { get; set; }
        public bool Active { get; set; }
    }
Expatiate answered 12/3, 2023 at 23:51 Comment(0)
M
0

If you are trying to get the [MemberNotNull] attribute mentioned in the other answer working with a .NET Framework project but have discovered that this attribute does not exist until .NET 5... there is now a Nuget package which has backported the attribute for .NET Framework:

https://www.nuget.org/packages/Hafner.Compatibility.NullableReferenceTypeAttributes

It requires C#9 and up.

Millican answered 12/4 at 20:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.