Search code examples
c#syntaxnull-forgiving-operator

C# ! (null-forgiving) not working as expected


I was looking over some code and suggested a fix for verifying something isn't null that can be null and suggested the following:

// Path: Employee.cs
public class Employee(string name, DateTime? arrivalTime = null) {
    public string Name = name;
    public DateTime? ArrivalTime = arrivalTime;
}
// Path: Program.cs
class Program
{
    static void Main(string[] args)
    {
        var testDate = DateTime.ParseExact(
            "2024-12-10 10:30:00",
            "yyyy-MM-dd HH:mm:ss",
            System.Globalization.CultureInfo.InvariantCulture
        );

        var employees = new List<Employee>([
            new Employee("Matt"),
            new Employee("John", testDate),
            new Employee("Steve")
        ]);

        foreach (var employee in employees)
        {
            if (employee.ArrivalTime == null) continue;

            var arrivalDate = DateOnly.FromDateTime(employee.ArrivalTime!);
            Console.WriteLine($"{employee.Name} arrives at {arrivalDate}.");
        }
    }
}

I assumed the null-forgiving operator would work. From my understanding, and reading the reference documentation it should suppresses the null value error. But I get the following error when compiling: "error CS1503: Argument 1: cannot convert from 'System.DateTime?' to 'System.DateTime'".

I'm using the .NET 8.0.404 SDK.

However, changing the code to

var arrivalDate = DateOnly.FromDateTime((DateTime)employee.ArrivalTime);

or

var arrivalDate = DateOnly.FromDateTime(employee.ArrivalTime ?? DateTime.Now);

makes it work fine.

Basically, my question is why does it not understand that a null suppression is safe in this context? My LSP understands that it's a nullable value if I remove the null check and warns me about it, but even with the null check it doesn't allow me to use the ! operator. Am I misunderstanding the operator usage?

Thanks in advance. :)


Solution

  • !(null-forgiving) not working as expected

    It actually is

    it should suppresses the null value error

    No, it suppresses the warnings coming from the null-state analysis performed by compiler. For example the following:

    foreach (var employee in employees)
    {
        var arrivalDate = DateOnly.FromDateTime(employee.ArrivalTime.Value);
        Console.WriteLine($"{employee.Name} arrives at {arrivalDate}.");
    }
    

    Will produce the warning:

    Warning CS8629 : Nullable value type may be null.

    Which can be suppressed with the ! operator:

    // will throw at runtime if ArrivalTime is null
    var arrivalDate = DateOnly.FromDateTime(employee.ArrivalTime!.Value);
    

    This operator is used when you know that the value will never be null here but compiler can't prove it and is a way to tell the compiler "shut up, I'm smarter" (which is obviously not always the case and I personally have shot myself in the foot several times with !)

    DateTime? is a nullable value type which is actually Nullable<DateTime> which does not have an implicit cast to DateTime, so you need to get DateTime which is usually done via the Value property of the Nullable<T> struct as was done in the preceding code.

    Read more: