使

使用 C# 8 非空引用类型编写更安全的代码 什么是非空引用类型?启用非空引用类型能给我们带来什么?迁移到可空引用类型 结论

2025-06-07

使用 C# 8 非空引用类型实现更安全的代码

什么是非空引用类型?

启用非空引用类型

这给我们带来了什么?

迁移到可空引用类型

结论

空引用异常是 .NET 应用程序中最常见的错误之一。尽管框架功能强大,但它的核心假设是引用类型可以指向 null,因此任何使用引用类型的代码都需要预先知道该对象不为 null,或者进行显式检查。

由于对象为空的情况通常很少发生,因此在开发甚至测试过程中很容易无法捕获此类错误。这意味着 .NET Framework 和 C# 语言的构建方式使得 bug 很容易隐藏在代码深处。

我们需要的是语言在这些问题发生之前告诉我们它们。

C# 8 的可空引用类型功能旨在解决此问题。

什么是非空引用类型?

首先声明一下——我们之前已经支持可空引用类型了。这就是问题所在。新特性实际上是我们必须使用的非空引用类型,但文档更倾向于将这些新特性称为可空引用类型

可空引用类型功能会接受所有现有的引用类型,并在编辑器级别默认假定其为非空。存在风险的代码仍可编译,但对于初始化为空、可能为空等情况,您会收到警告。

空引用警告

因为 null 仍然是一个重要的概念,所以您仍然能够表示潜在的空类型,但您必须明确地说该类型可以为空,因为引用类型默认被视为非空。

如果您熟悉 TypeScript 中的可空性,这与TypeScript 的工作方式有点类似。

启用非空引用类型

要启用此功能,至少在撰写本文时,您必须手动编辑要启用该功能的每个项目的 .csproj 文件。您还需要使用 Visual Studio 2019 或 .NET Core 3.0 或更高版本。

具体来说,您需要向任何PropertyGroup:LangVersionNullable:添加两个新属性

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <AssemblyName>MattEland.SoftwareQualityTalk</AssemblyName>
    <RootNamespace>MattEland.SoftwareQualityTalk</RootNamespace>      

    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable> 

  </PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

LangVersion 告诉 C# 使用 C# 8 编译器。

Nullable 告诉 C# 假定所有声明的引用类型默认都是非空的。

这给我们带来了什么?

当您激活可空引用类型时,您将在 Visual Studio 中看到很多警告,表明各种值可能为空或返回空。

具体来说,你会看到很多类似这样的警告:

返回 null 时的警告

这指出了上述方法声明了它返回一个ResumeKeyword返回类型。之前,默认情况下,返回类型为可空引用类型,现在除非另有说明,否则默认为非空。为了解决这个问题,我们将返回类型更改为 ,ResumeKeyword??指明该实例可能不存在。

正确的代码如下:

private static ResumeKeyword? FindKeyword(IDictionary<string, ResumeKeyword> keywordBonuses, string key)
{
    if (keywordBonuses.ContainsKey(key)) 
    {
        return keywordBonuses[key];
    }

    return null;
}
Enter fullscreen mode Exit fullscreen mode

另一个你会看到大量警告的原因是非空属性和变量未初始化。为了解决这个问题,你可以为它们分配一个合适的初始值,或者使用我们?上面用到的运算符更改它们的变量类型,以指示其值为空。

需要注意的是,非空引用类型不会从根本上改变生成的代码,因为它们只是为了方便开发。但它带来的附加价值以及代码的简洁性是巨大的,值得投入。

迁移到可空引用类型

首次打开可空引用类型时,您可能会在代码中看到大量类似上述的警告。

如果需要一点一点地迁移,则可以使用新的#nullable disable预处理器指令恢复代码某些部分的旧行为,如下例所示:

#nullable disable
public class KeywordData
{
    public int Id { get; set; }
    public string Keyword { get; set; }
    public int Modifier { get; set; }
}
#nullable restore
Enter fullscreen mode Exit fullscreen mode

或者(这是微软推荐的方法),除非你已经迁移过来支持可空引用类型的类,否则你不能在项目中启用它们。在这种情况下,你可以使用#nullable enable而不是disable。这有助于逐步迁移代码,但代价是你的整个代码中可能会出现大量的可空指令。

该代码如下所示:

#nullable enable
public class KeywordData
{
    public int Id { get; set; }
    public string? Keyword { get; set; }
    public int Modifier { get; set; }
}
#nullable restore
Enter fullscreen mode Exit fullscreen mode

有关升级现有代码以使用可空引用类型的更多全面信息,请参阅Microsoft 的升级指南。

结论

我强烈建议您小规模地尝试一下这个功能,看看效果如何。鼓励您明确代码行为,可以大大提升缺陷的消除潜力。

如果您想要一种更严格的方法来在编译器级别强制执行可空性,请阅读我关于使用 Language-Ext 库中的 Option 类在编译器级别捕获问题的文章,但请注意,语法比 C# 8 非空引用类型要麻烦得多。

如果您喜欢使用属性来注释参数和属性的可空性的想法,请查看我关于 JetBrains.Annotations 包的文章


总的来说,我喜欢 C# 8 中非空引用类型的简洁性,我会继续使用它们,直到找到更好的方案。如果您想了解更多信息,请阅读官方文档

如果您发现其他更适合您构建高质量软件的方法,我很乐意听到,因为我一直在寻找更好的方法来消除所有类型的软件缺陷

文章来源:https://dev.to/integerman/safer-code-with-c-8-non-null-reference-types-4f2c
PREV
软件质量深度防御
NEXT
内向者公开演讲的技巧:自信与能力 演讲作为成长机会 重塑演讲体验 登上舞台