3

Updated my .NET Core 3.1 console application to use Nullables feature and have the following foreach loop.

DataTable profiles = DB.ListProfiles();
// CS8600 warning here
//               v
foreach (DataRow r in profiles.Rows)
{
    // processing code here
}


static public DataTable ListProfiles()
{
    DataTable t = new DataTable();
    using (SqlConnection conn = new SqlConnection(connString))
    {
        conn.Open();
        using (SqlCommand cmd = conn.CreateCommand())
        {
            cmd.CommandText = "SELECT * FROM [Table]";
            using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                sda.Fill(t);
        }
    }

    return t;
}

In this example, r has the compiler warning.

Converting null literal or possible null value to non-nullable type.

I am aware of the need to check for nulls and such. My question is has anyone figured out how with this new feature to get the compiler to realize that neither profiles nor profiles.Rows will ever be null? The t variable in ListProfiles could absolutely return no results, but that wouldn't make Rows null, would it?

If I use the null-coalescing operator ?? like this: profiles.Rows ?? ... and the warning would go away but I haven't been able to figure out what can go after it to actually work.

I have also tried using the ! operator from How to suppress Possible Null Reference warnings but that has no effect. The warning remains. While this works for other types, it doesn't seem to work for DataRowCollection.

I have tried various placements of null checks but only an explicit in-code suppression seems to have any effect.

ahwm
  • 672
  • 9
  • 23

1 Answers1

4

The nullable value is r, not profiles or profiles.Rows, as correctly identified by the compiler. Just declare it as nullable, then handle the null check in the loop:

foreach (DataRow? r in profiles.Rows)
{
    if(r == null) 
        continue;
    // ...
}

The reason is that DataRowCollection (which is the type of the Rows property on the data table) is a non generic enumerable. That's what you get for using .NET 1.0 APIs.

The compiler considers the items of non generic enumerables to be of type object?, not object (or more accurately, IEnumerator.Current is an object?), because any such item in the collection could be null, and there are no generics to restrict the items to non nullable values.

Since specifying the type of a foreach variable simply inserts a cast for you, the end result is you need to declare the variable itself as nullable, since you can't safely cast a nullable value to a non nullable one.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
  • Thank you. That makes a lot of sense. If this were more than just a super simple console app I would look at using something other than `DataTable` but it's just not a big enough project. – ahwm Aug 21 '20 at 15:31