C#9Nullable型の問題

Sep 12 2020

次の(VS16.8.0プレビュー2.1C#9.0プレビュー)コードを検討してください。

#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 
  }
}

F3のエラーを理解/対処するのに苦労しています。コンパイラはlong、long?(VSでホバーして確認したように)johnAgeが存在しないと考えArchive<T>.GetAtているようです。T?

私が望むことを実行する汎用アーカイブを作成する方法はありますか(Tがnull許容ではない基本型、つまりlongの場合でもNullableを返すGetAtメソッド)?

回答

8 JonSkeet Sep 12 2020 at 15:13

基本的に、これはnull許容値型とnull許容参照型が非常に異なることになります。CLRはnull許容値型を認識しますが、CLRに関する限り、null許容参照型は「通常の参照型であり、コンパイラにnull許容と見なすべきかどうかを示す属性があります」。

に制約Tがあるnotnull場合、型はILでT?コンパイルさTれるだけです。制約は値型であるNullable<T>ため、コンパイルできません。Nullable<T>T

したがって、の場合、キーがディクショナリに見つからない場合Archive<long>、GetAtメソッドは0Lを返します-コードが効果的に期待しているものである、のnull値を返しません(そして返すことができません)。Nullable<long>F3

「null許容参照型」機能全体は、基本的にそれを持たない型システム上にnull許容認識の「ベニア」を追加しようとする試みに悩まされています。新しいランタイムと言語が今ゼロから一緒に設計されていたとしたら、これをより緊密に統合しようとするでしょう。現状では、この機能にはまだ多くの価値があると思いますが、ジェネリックに関しては間違いなく非常に注意が必要です。