【问题标题】:C# 9 Nullable types issuesC# 9 可空类型问题
【发布时间】:2020-09-12 06:48:29
【问题描述】:

考虑以下(VS 16.8.0 Preview 2.1 C# 9.0 preview)代码:

#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 中的错误, 似乎编译器认为 johnAge 存在 long 而不是 long?(正如我在 VS 中将鼠标悬停在它上面验证的那样),尽管返回的 Archive&lt;T&gt;.GetAtT?

有没有办法拥有一个通用的存档来做我想要的(一个 GetAt 方法,即使 T 是一个不可为空的基本类型,即 long 也返回 Nullable)?

【问题讨论】:

  • 我在你的另一个问题中发布了这个,我通读了它,它帮助我理解了一点:stackoverflow.com/questions/58229782/…所有答案都是相关的。
  • 可能是因为在value : default中,default代表的是0(default(T)),而不是null(default(T?))。
  • 如果您在每个帖子中坚持 一个 问题(最好格式化它,这样我们就不需要横向滚动),这会有所帮助。您实际上希望在这篇文章中回答哪个示例?请编辑问题,使其解决该问题。您可以随时针对其他问题发布另一个问题。
  • @SandeepPandey -- 这就是我们在业内喜欢称之为kludge的东西。
  • 好的,它正在在 VS 中为我​​编译,而不是从命令行编译。预览的乐趣。我相信我现在可以添加答案。

标签: c# c#-8.0 c#-9.0


【解决方案1】:

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

T 具有notnull 约束时,T? 类型只会在 IL 中编译为 T。它必须 - 它不能编译为 Nullable&lt;T&gt;,因为 Nullable&lt;T&gt; 约束 T 是一个值类型。

所以对于Archive&lt;long&gt;,如果在字典中找不到键,GetAt 方法将返回 0L - 它不会(也不能)返回空值Nullable&lt;long&gt;,这是您在 F3 中的代码实际上所期望的。

整个“可为空的引用类型”特性受到试图在根本上没有它的类型系统上添加可空意识的“外衣”的尝试。我敢肯定,如果现在从头开始设计一种新的运行时和语言,它会尝试更紧密地统一这一点。事实上,我相信这个特性仍然有很大的价值——但它确实让泛型的事情真的变得棘手。

【讨论】:

  • 谢谢乔恩!有没有办法让我得到我想要的?那是一个返回 T 的 GetAt 方法?这样,如果我不检查 null,我会收到警告,并且对 Manager 和 long 工作相同?
  • @kofifus:不,如第二段所述。返回类型可以是TNullable&lt;T&gt; - 它不能同时是两者,并且当T 是引用类型时后者无效。不幸的是,这只是可空引用类型令人讨厌的尖角之一。您可以创建一个Archive&lt;T&gt;子类,将T 限制为struct,并在那里创建一个新方法……但这会有自己的尖角。
  • 关于你上面的解释(..当 T 有 notnull 约束..)我注意到即使我删除了通用的 notnull 约束,我也会得到完全相同的结果
  • @kofifus:不同之处在于您将无法创建Archive&lt;int?&gt;Archive&lt;string?&gt;。我仍然对 Archive&lt;T&gt; 完全编译感到惊讶——要么这是预览中的错误,要么是 C# 9 中对 NRT 的更改。
猜你喜欢
  • 2010-09-30
  • 1970-01-01
  • 2023-03-21
  • 2021-02-17
  • 2010-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多