Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# 9 Nullable types issues

Tags:

c#

c#-8.0

c#-9.0

Consider the following (VS 16.8.0 Preview 2.1 C# 9.0 preview) code:

#nullable enable

using System.Collections.Generic;

class Archive<T> where T : notnull 
{
  readonly Dictionary<string, T> Dict = new();

  public T? GetAt(string key) 
  { 
    return Dict.TryGetValue(key, out var value) ? value : default;
  }
}

class Manager 
{
  public int Age { get; set; }
}

class Main34 
{
  long F3() 
  {
    Archive<long> a = new();
    var johnAge = a.GetAt("john");
    if (johnAge is null) return -1; // Error CS0037  Cannot convert null to 'long' because it is a non - nullable value type
    return johnAge; 
  }

  long F4() 
  {
    Archive<Manager> a = new();
    var johnAge = a.GetAt("john");
    //if (johnAge is null) return -1;
    return johnAge.Age; // Correct ! warning "Derefrencing of a possibly null reference" will be removed if line above unremarked 
  }
}

I am having a hard time understanding/addressing the errors in F3, Seems like the compiler thinks johnAge there is long not long? (as I verified by hovering over it in VS) despite the return of Archive<T>.GetAt being T?

Is there a way to have a generic Archive which will do what I want (a GetAt method that return Nullable even when T is a non nullable basic type ie long) ?

like image 277
kofifus Avatar asked Sep 12 '20 06:09

kofifus


Video Answer


1 Answers

Fundamentally, this comes down to nullable value types and nullable reference types being very, very different. The CLR is aware of nullable value types, but as far as the CLR is concerned, nullable reference types are just "the normal reference type, with an attribute telling the compiler whether or not it should be considered as nullable".

When T has the notnull constraint, the type T? just compiles to T in the IL. It has to - it can't compile to Nullable<T>, because Nullable<T> constraints T to be a value type.

So for an Archive<long>, the GetAt method will return 0L if the key isn't found in the dictionary - it won't (and can't) return the null value of a Nullable<long>, which is what your code in F3 is effectively expecting.

The whole "nullable reference types" feature suffers from being an attempt to add a "veneer" of nullable awareness over a type system that fundamentally doesn't have it. I'm sure if a new runtime and language were being designed together from scratch now, it would try to unify this more closely. As it is, I believe the feature still has a lot of value - but it definitely makes things really tricky when it comes to generics.

like image 95
Jon Skeet Avatar answered Oct 18 '22 22:10

Jon Skeet