Search code examples
c#.net-coreopenxmlnullable

Unable to safely detect null value without nullable warnings


I don't quite understand the following nullable warning.

As you can see, I have part of an expression where sheet is not null, but sheet.Id may be. But why can't I used sheet.Id.HasValue to find out?

enter image description here

enter image description here

I also tried sheet.Id != null, but that gives me a different warning.

Warning CS8625 Cannot convert null literal to non-nullable reference type.

How can I safely determine if sheet.Id is not null?

Update

This version actually compiles without warning. But I still don't understand why the other versions give me warnings.

public Worksheet? GetFirstWorksheet()
{
    WorkbookPart? workbookPart = Document.WorkbookPart;
    Workbook? workbook = workbookPart?.Workbook;
    if (workbook != null)
    {
        Sheets? sheets = workbook.GetFirstChild<Sheets>();
        Sheet? sheet = sheets?.Elements<Sheet>()
            .FirstOrDefault();
        if (sheet != null && sheet.Id?.Value != null)
            return ((WorksheetPart)workbookPart!.GetPartById(sheet.Id!)).Worksheet;
    }
    return null;
}

Solution

  • You can combine

            if (sheet != null && sheet.Id?.Value != null)
                return ((WorksheetPart)workbookPart!.GetPartById(sheet.Id!)).Worksheet;
    

    into a single equivalent expression:

             if (sheet?.Id?.HasValue == true)
                return ((WorksheetPart)workbookPart!.GetPartById(sheet.Id!)).Worksheet;
    

    or

             if (sheet?.Id?.Value != null)
                return ((WorksheetPart)workbookPart!.GetPartById(sheet.Id!)).Worksheet;
    

    I am not sure why the compiler is not smart enough to realize that the in some terms of your expression some of the fields could no longer be null.

    By the way, depending on how far down the nullable chaining rabbit hole you want to go, you could rewrite the code from the images to look look something like this:

    Worksheet? GetWorksheet(string name)
    {
        WorkbookPart? workbookPart = Document.WorkbookPart;
        Worksheet? sheet = workbookPart?.Workbook
            .GetFirstChild<Sheets>()?.Elements<Sheet>()
            .Where(s => string.Compare(s.Name, name, true) == 0 && s.Id?.HasValue == true)
            .Select(s => ((WorksheetPart) workbookPart!.GetPartById(s.Id!)).Worksheet)
            .FirstOrDefault();
        return sheet;
    }
    

    or

    Worksheet? GetWorksheet(string name)
    {
        WorkbookPart? workbookPart = Document.WorkbookPart;
        return workbookPart?.Workbook
            .GetFirstChild<Sheets>()?.Elements<Sheet>()
            .Where(s => string.Compare(s.Name, name, true) == 0 && s.Id?.HasValue == true)
            .Select(s => ((WorksheetPart) workbookPart!.GetPartById(s.Id!)).Worksheet)
            .FirstOrDefault();
    }