C#9可空类型问题

问题描述

考虑以下(VS 16.8.0预览版2.1 C#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?

,但似乎编译器认为johnAge是Archive<T>.GetAt而不是T?(正如我在VS中将其悬停所验证的)

是否有一种方法可以实现我想要的通用存档(即使T是不可为空的基本类型(即long),也将返回Nullable的GetAt方法)?

解决方法

从根本上讲,这可归结为可空值类型和可空引用类型非常不同。 CLR知道可为空的值类型,但是就CLR而言,可为空的引用类型只是“普通引用类型,其属性告诉编译器是否应将其视为可为空”。

T具有notnull约束时,类型T?仅在IL中编译为T。它必须-无法编译为Nullable<T>,因为Nullable<T>T约束为值类型。

因此,对于Archive<long>,如果在字典中找不到键,则GetAt方法将返回0L-它不会(并且无法)返回Nullable<long>的空值,这是您在F3中的代码有效的期望值。

整个“可为空的引用类型”功能遭受了试图在根本没有它的类型系统上添加可为空的意识的“标尺”的尝试。我敢肯定,如果现在是从头开始一起设计新的运行时和语言,它将尝试更紧密地统一这一点。就目前而言,我相信该功能仍然具有很大的价值-但在泛型方面,确实确实使真正变得棘手。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...