2

I trying to create class with some number of non-nulable properties (>7).

If I do like this:

public class Meeting
{

    public Name Name { get; set; }
    public Person ResponsiblePerson { get; set; }
    public Description Description {get; set; }
    public Category Category { get; set; }
    public Type Type { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
    public List<Person> Attendees { get; set; }


    public Meeting()
    {
        Attendees = new List<Person>();
    }
}

I get warnings like this: "Non-nullable property '...' must contain a non-null value when exiting constructor. Consider declaring the property as nullable."

There is a list of variants I thinked of:

  • Default values removes this warning, but I think, that it will just add useless values, that in the future will be needed to be changed anyway.

  • If I init all properties in constructor this warning gets away, but then constructor will have >7 params which is nightmare.

  • I have seen that some people use "Builder" pattern to init classes with many params. But the builder itself can't prevent 'null' values (The builder will get same warnings).

  • There is easy way to remove warning, by making nullable property in .csproj file from true to false, but I don't want to just remove that warning. I want to make class itself safe from null values.

Whats clean way create that class?

Yoro
  • 805
  • 8
  • 17
  • 2
    So, is the idea that whoever instantiates that class will initialise each and every one of those properties? – canton7 Apr 12 '22 at 08:12
  • 1
    If the properties cannot be null then you either must initialise them with default non-null values, or require non-null values to be passed to the constructor and set the properties there. Anything else will allow the object to contain null properties after construction. – Matthew Watson Apr 12 '22 at 08:12
  • If you're certain that the person who instantiates that object will assign all of those properties, you can forcefully initialize them to null, with `{ get; set; } = null!;` (or wait for required properties in C# 11) – canton7 Apr 12 '22 at 08:18
  • 2
    What problem are you trying to solve? – Stuart.Sklinar Apr 12 '22 at 08:23
  • @canton7 Kinda yes, kinda no. At the initialization class can be half empty, but I need to be sure that before serialization (Or smth else) that every property will eventually have these values – Yoro Apr 12 '22 at 08:27
  • 1
    Does this answer your question? [Non-nullable property must contain a non-null value when exiting constructor. Consider declaring the property as nullable](https://stackoverflow.com/questions/67505347/non-nullable-property-must-contain-a-non-null-value-when-exiting-constructor-co) – GazTheDestroyer Apr 12 '22 at 08:28
  • @Yoro Currently, the only way to ensure that all properties are non-null when the object is instantiated is to use a constructor. `required` properties are coming in C# 11, which will improve this. – canton7 Apr 12 '22 at 08:36
  • @canton7 can you give a link where I can read of future *required*? That's interesting. – Yoro Apr 12 '22 at 08:41
  • @Yoro https://github.com/dotnet/csharplang/issues/3630 – canton7 Apr 12 '22 at 08:51
  • *Whats clean way create that class?* - use a record? – Caius Jard Apr 12 '22 at 08:52
  • @canton7 Looks cool. I found this with good examples https://exceptionnotfound.net/bite-size-csharp-11-required-properties/ That would be perfect solution. – Yoro Apr 12 '22 at 08:55
  • [See here for Roslyn language feature status](https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md) - The item you're looking for is "Required Members" in the table. – Matthew Watson Apr 12 '22 at 09:03
  • @CaiusJard If I understood correctly, editing a *record class* properties after init is impossible without creating new one. I need ability to edit. – Yoro Apr 12 '22 at 09:39
  • records are immutable by default, but they don't have to be – Caius Jard Apr 12 '22 at 09:55
  • Right, but if you have a mutable record, you can't make use of primary constructors, which means OP isn't gaining anything here – canton7 Apr 12 '22 at 09:58

2 Answers2

4

Since C#11 you can add a Required modifier.

public class Meeting
{
    public required Name Name { get; set; }
    public required Person ResponsiblePerson { get; set; }
    public required Description Description {get; set; }
    public required Category Category { get; set; }
    public required Type Type { get; set; }
    public required DateTime StartDate { get; set; }
    public required DateTime EndDate { get; set; }
    public List<Person> Attendees { get; set; } = new List<Person>();
}

This feature aims to improve how we initialize objects that do not rely on constructor parameters.

More detailed info: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required

Yoro
  • 805
  • 8
  • 17
  • > Required members must be initialized, but they may be initialized to null. So with `required` you can't escape someone setting a null value and get the same warning there as the property was meant to be non-nullable – Ashraf Ali Jun 08 '23 at 22:10
0

The whole point of non-nullable is to initialize class instances in a "valid" state. If Meeting is part of a domain, having some logic inside, you can't initialize Meeting without initial values. I.e., new Meeting() doesn't make any sense. On the other hand, if Meeting is a so-called data transfer object and it may be initialized partially, if not even empty, then setters are fine, and you don't need a constructor.

  • So as I understand the best practice is to configure "non-nullable" warnings to ignore DTO's directory? – Yoro Apr 14 '22 at 10:06
  • 1
    No, it would beat the purpose. I wouldn't go for warning suppression. Instead of that you should explicitly declare property as nullable. See @GazTheDestroyer comment at your question. – Karlis Fersters Apr 14 '22 at 15:35