Search code examples
c#.netasp.net-core.net-8.0

C# required property setter for reference type with default value set to null


In this document why would Microsoft show creating a model where required properties are set to null? I started using this code for all my DTO models where I have required strings but I figured I better find out what it means first. Unfortunately I can't find anyone talking about this on the web.

Here is how they show creating a model:

public class Movie
{
    [Required]
    [StringLength(100)]
    public string Title { get; set; } = null!;

Why would you say the Title is required but then default it's value to null?

It seems like to me you would normally write this logic like this:

public class Movie
{
    [StringLength(100)]
    public string? Title { get; set; }

I guess I'm wondering about 2 issues

  1. Why would you add the [Required] attribute to a nullable property.
  2. When do you use this syntax { get; set; } = null!;?

Solution

  • but then default it's value to null?

    This is done to prevent the nullability warnings which will be produced by compiler during the null-state analysis (since just calling new Movie() will result in nulls assigned to the corresponding properties, ! is so called null-forgiving operator which forces compiler to ignore the warnings, so you can use null as a default value).

    Usually such code comes from assumption that class will be used only as DTO for binding the incoming request and should not be directly instantiated in the code (except for tests).

    Note that since C# 11 required keyword can be used instead of the attribute:

    The required modifier indicates that the field or property it's applied to must be initialized by an object initializer. Any expression that initializes a new instance of the type must initialize all required members. The required modifier enables developers to create types where properties or fields must be properly initialized, yet still allow initialization using object initializers.

    public class Movie
    {
        [StringLength(100)]
        public required string Title { get; set; }
    }
    

    ASP.NET Core recognizes the modifier and performs corresponding validations during binding. Arguably using it makes model clearer and more robust.

    Notes:

    1. Nullable reference types (NRTs) are not actually separate types in C# (unlike Nullable value types ) and when enabled the fact that reference type is not marked as nullable (i.e. string) does not actually guarantee that the value is not null (it can be as you can see with the code from the docs, when you call new Movie()). As mentioned before - NRTs are used for null-state analysis by compiler (to reduce probability of NullReferenceException) and also they are processed by parts of the framework (like by ASP.NET Core or EF Core).
    2. RequiredAttribute with value types can have some unexpected behavior - Validation with data annotations does not work
    3. required modifier can have a bit unexpected behavior when used "directly" with System.Text.Json (see Throw exception when missing not nullable value in System.Text.Json).