C# 项目的 nullable 检查

C# nullable 按照修饰变量类型不同, 分成两种情况:

1. nullable value type,  照常理来讲, value 类型的变量不能设为null, 在C# 2.0为了方便程序员使用基础类型变量来存储DB null值, 为value type引入的 nullable 特性, 比如 int? a=null , 这里的 int? 相当于 Nullable<int>.

2. nullable ref type, 照常理来讲, ref 类型变量天然可以设置为null, 但当今计算机语言界认为这种隐含的可以赋值为null的变量, 会引起很多预期之外的问题, 所以现在的编程语言都倾向于更严格限制null,  C# 8为了响应这一潮流也对ref type 可空性做了限制,

   简言之,  ref type的nullable特性, 是用来限制 ref type 赋值为 null,  尽量规避 NullReferenceException 异常;    而 value type的 nullable 特性为值类型增加可空的能力, 因为是基于值类型, 所以并不会意外产生 NullReferenceException 异常. 

 

下面是一个table和对象的字段映射定义,

CREATE TABLE [dbo].[People](
    [Name] [nvarchar](50) NOT NULL,
    [BirthDate] [datetime] NULL,
    [FavoriteMovie] [nvarchar](50) NULL,
    [FavoriteNumber] [int] NULL
) ON [PRIMARY]


//BirthDate and FavoriteNumber are nullable value types (nullable DateTime and nullable int respectively).
//FavoriteMovie is a string, which is nullable (it’s a reference type).
public class Person { public string Name { get; set; } public DateTime? BirthDate { get; set; } public string? FavoriteMovie { get; set; } public int? FavoriteNumber { get; set; } }

 

 

在C#7(含7)之前 reference 变量可以为null , 但在C#8为了尽量避免 NullReferenceException 异常,引入了breaking change, 默认情况下reference 不可为null.

//C# 7
private string str=null ;

//C# 8
private string str; //不能为null

//C# 8, 如果需要为空
private string? str=null ;

.Net6 生成的项目, 默认会启用 nullable 检查, 即对于 ref 类型限制 null 赋值, 以便尽可能避免 NullReferenceException 异常, 但这仅仅是个编译警告, 并不会导致编译错误.
 csproj 文件设置. 如果要上升到报错级别, 需要设置 <TreatWarningsAsErrors>true</TreatWarningsAsErrors>  , 为了防止编译器报错太多无关紧要的告警, 可以通过<NoWarn>将这些报警编号忽略掉.
<PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
<NoWarn>CS8618</NoWarn>
</PropertyGroup>

Nullable 检查机制底层是通过 nullable context来实现的,  该上下文提供两种特性, 第1特性是将string? 这样的类型编译到二进制文件中, 即二进制文件中保留  annotations metadata, 第2个特性是, 编译过程会生成相关的告警.

所以 <Nullabe> 的取值有 enable/disable/warnings/annotations 4种, 4种类型涉及两个维度, 警告上下文和注解上下文的开启, 

enable: 同时开启nullable context的两个特性

disable: 完全关闭 nullable context 的两个特性,  代码中也不允许通过 string? 来定义变量.

warnings:  关闭注解上下文, 仅仅开启警告上下文, 简单理解: 编译器不会将nullable相关的类型 metadata 编译到二进制文件, 但编译过程对于违反nullable规则,  会报 warning. 

annotations: 开启  annotations 特性, 但关闭编译器告警. 

 

实际项目的问题:

 

 

 

问题1: 我们能确定赋值不会为null, 但编译器推断为空, 怎么调教编译器呢?

方案: 在代码中加上下面的方法即可. 

ArgumentNullException.ThrowIfNull(keyValuePairs);

 

问题2:  [DisallowNull] 注解和 T 的区别, [AllowNull] 和  T? 和Nullable<T> 的区别:  

[DisallowNull] string a1="a" ;   string a2="a"; 

上面代码在C#8是没有区别的,  [DisallowNull] 常用于泛型类定义.

 

[AllowNull] string a1="a" ;   string? a2="a";  Nullable<string> a3="a";

上面代码在C#8是没有区别的,  [AllowNull] 常用于泛型类定义.

 

问题3: 强制干预编译器赋null值 

string a = null;     // warning
string b = null!;    // ok
string c = default!; // ok

 

问题4: 如何为老项目启用nullable检查

csproj文件中, 启用nullable检查, 

然后先为每个cs文件加上下面的指令

#nullable disable

然后一个一个文件开启, 并修正代码. 

 

=================================

参考

=================================

C# 8: Nullable Reference Types - Meziantou's blog

C# 8.0 如何在项目中开启可空引用类型的支持 - walterlv 

可为空引用类型 | Microsoft Docs

posted @ 2021-11-16 23:33  harrychinese  阅读(3713)  评论(3)    收藏  举报