C#8.0 可空引用类型
介绍
我们的项目代码运行时最频繁的错误之一就是 System.NullReferenceException 异常,c#8.0增加的可为空引用类型就是用来帮助开发者降低甚至消除NULL异常。我们需要注意的是可空引用类型是语法级别的功能,也就是代码编写的时候就会受到编程约束,这个与可为空值类型是不一样的。项目支持c#8.0请参见C# 语言版本控制。
目录
在项目中启用可空引用类型支持
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
在项目文件中增加<Nullable>enable</Nullable>后,项目代码中的引用类型将被解析拆分为不可空引用类型和可空引用类型。
将警告提升为异常
可空引用类型功能是以警告的形式出现,并不会干扰项目生成编译,约束力较弱。如果想严格要求自身,那我们可将特定的警告变为异常来提升约束力。
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<WarningsAsErrors>
$(WarningsAsErrors);CS8600;CS8601;CS8602;CS8603;CS8604;CS8609;CS8610;CS8614;CS8616;CS8618;CS8619;CS8622;CS8625
</WarningsAsErrors>
</PropertyGroup>
</Project>
相关技术文档C# 编译器选项 - 错误和警告 | Microsoft Docs,Non-nullable references with C# 8 and .NET Core 3.0 · Cezary Piątek Blog (cezarypiatek.github.io),大家在编写代码时遇到Microsoft.CodeAnalysis.CSharp分析器所给的警告代码,都可按照自己的要求将其变为异常来约束自己。
将变量标注为可空引用类型
我们平时使用的引用类型属于不可空引用类型,在其后附加?便为可空引用类型。
string name; //不可空字符串
string? adress; //可空字符串
泛型
public TKey GetKey<TKey>()
{
//必须返回不可空类型
}
public TValue? GetValue<TValue>()
{
//可返回可空类型
}
使用示例

如上示例,由于Student拥有默认的空构造函数new Student(),此构造函数会使Name和Adress属性为null,所以分析器发出了CS8618的警告。

我们将空构造函数写上,此时警告智能的转移到构造函数上了。

我们在构造函数中将可能为null的string类型属性附上值,警告消除。而string?类型无需处理,因为它是允许为null的。


以上两种方式也可以消除警告。

在GetStudentNames方法中,我们使用Student的EnglishName属性时,分析器发出了CS8604警告,因为EnglishName属性是可空引用类型,无法放入List<string>中,只能放入在List<string?>中。

我们使用??判断当EnglishName为null时,使用不可空引用类型属性Name,此时CS8604警告消除。
进阶
可空引用类型模式中,属性是可以被拆分为两种模式的,其一是属性是否可被赋值null,其二是属性的值是否可能为null。大家可能对这句话理解起来有点懵,请接着看下面的讲解。
[AllowNull]
不可为null的引用类型属性允许被赋值null

上面代码中,Adress属性即使被赋值null,也不会使其值为null,不会在代码中引发潜在的Null异常。所以此场景是合理且被允许的。
[DisallowNull]
可为null的引用类型属性不允许赋值为null

Adress属性虽然默认值是null,但对其赋值null是不合理的。虽然不能赋值null,但获取Adress属性的值时仍可能为null,大家可在合适的场景使用[DisallowNull]。
[NotNull]
可为null的引用类型属性的值永远不会是null,可放心使用


我们使用GetStudentAdress方法返回Student的Adress属性,分析器并没有发出警告,因为分析器通过[NotNull]特性也知道了Adress属性的值永远不会为null。

我们尝试将Adress属性改为可能返回null值,分析器立马发出了CS8603警告,很给力。
[NotNullIfNotNull]
这个特性作用于方法中,用于告诉其他程序员只要你不给我的方法传null参,我就不会返回null给你,你看着办。
[return: NotNullIfNotNull("student")]
public string? GetStudentAdress(Student? student)
{
return student?.Adress;
}

adress和adress2有着不同的待遇。
缺陷
有些场景分析器无法分析出潜在的null异常
Struct
public struct Student
{
public string FirstName;
public string? MiddleName;
public string LastName;
}
public static class Program
{
public static void PrintStudent(Student student)
{
Console.WriteLine($"First name: {student.FirstName.ToUpper()}");
Console.WriteLine($"Middle name: {student.MiddleName?.ToUpper()}");
Console.WriteLine($"Last name: {student.LastName.ToUpper()}");
}
public static void Main() => PrintStudent(default(FirstName));
public static void Main2() => PrintStudent(new Student());
}
default(FirstName) 和new Student()中的FirstName 和 LastName 运行时为 null,编辑器此时未出现任何警告。
public struct Foo<T>
{
public T Bar { get; set; }
}
public static class Program
{
public static void Main()
{
string s = default(Foo<string>).Bar;
string s2 = new Foo<string>().Bar;
}
}
属性 Bar 在运行时为 null,而s和s2是不可为null字符串类型,编辑器此时未出现任何警告。
数组
数组也是可为 null 的引用类型中的已知缺陷
using System;
public static class Program
{
public static void Main()
{
string[] values = new string[10];
string s = values[0];
Console.WriteLine(s.ToUpper());
}
}
代码中的数组声明其元素为不可为null的string,而其元素在初始化时都为null,编辑器此时未出现任何警告。
总结
将引用类型拆分为可空引用类型和不可空引用类型可以为我们的项目代码带来质的提升,团队之间协作或者使用第三方的类库都可以通过?标识来知道方法的某个参数传null不会引发异常、属性赋值null不会引发异常,反之我们使用某些属性或者方法的返参也可以知道其是否可能为null,对于不可能为null的变量我们就无需再麻烦的检测null值了,而在以前,我们可能需要对每个变量都需要做null判断。感兴趣的同学赶紧给自己的项目加入这个功能吧。
我们正在行动,新的框架、新的生态
我们的目标是自由的、易用的、可塑性强的、功能丰富的、健壮的。
所以我们借鉴Building blocks的设计理念,正在做一个新的框架MASA Framework,它有哪些特点呢?
- 原生支持Dapr,且允许将Dapr替换成传统通信方式
- 架构不限,单体应用、SOA、微服务都支持
- 支持.Net原生框架,降低学习负担,除特定领域必须引入的概念,坚持不造新轮子
- 丰富的生态支持,除了框架以外还有组件库、权限中心、配置中心、故障排查中心、报警中心等一系列产品
- 核心代码库的单元测试覆盖率90%+
- 开源、免费、社区驱动
- 还有什么?我们在等你,一起来讨论
经过几个月的生产项目实践,已完成POC,目前正在把之前的积累重构到新的开源项目中
目前源码已开始同步到Github(文档站点在规划中,会慢慢完善起来):
QQ群:7424099
微信群:加技术运营微信(MasaStackTechOps),备注来意,邀请进群

------ END ------
作者简介
吴炜来:MASA技术团队成员。
C#8.0 可空引用类型的更多相关文章
- C#8.0可空引用类型的使用注意要点
最近VS2019正式版发布了,装下来顺便试用了一下C#8.0,最大的看点应该就是可空引用类型了.不过C#8.0仍然处于Beta的状态,而且试用时也遇到了几个坑. 背景知识说明: 所谓的可空引用类型是指 ...
- C#8.0—非空引用类型
非空引用类型--C#8.0 原文地址:https://devblogs.microsoft.com/dotnet/try-out-nullable-reference-types/?utm_sourc ...
- 迫不及待地体验了一把 C#8.0 中的可空引用类型(Nullable Reference)
在我之前的一篇博客 NullReferenceException,就不应该存在! 中,我吐槽了 C# 中 null 的弊端以及避免 null 的方法:事实上这本都是现代高级语言中极力推崇的做法.Kot ...
- 快速了解C# 8.0中“可空引用类型(Nullable reference type)”语言特性
Visual C# 8.0中引入了可空引用类型(Nullable reference type),通过编译器提供的强大功能,帮助开发人员尽可能地规避由空引用带来的代码问题.这里我大致介绍一下可空引用类 ...
- C# 可空引用类型
可空引用类型是C#8.0计划新增的一个功能,不过已经发布了预览版本,今天我们来体验一下可空引用类型. 安装 您必须下载Visual Studio 2017 15.5预览版(目前最新发布版本是15.4) ...
- C# 8.0 可空(Nullable)给ASP.NET Core带来的坑
Nullable reference types(可为空引用类型) 可为空引用类型不讲武德 C#8.0 引入了"可为空引用类型"和"不可为空引用类型",使我们能 ...
- (基础篇) php中0与空 Null false的区别
<?php $test=0; if($test==''){ echo '<br />在php中,0即为空'; //被输出 } if($test===''){ echo '<br ...
- PHP中0、空、null和false的总结
php中很多人还不懂php中 0 , '' , null 和 false 之间的区别,这些区别有时会影响到数据判断的正确性和安全性,给程序的测试运行造成很多麻烦.另外在面试题中也会遇到这些问题,如下: ...
- 弱类型语言中的0和空字符串(''或"")以及字符串'0'
在弱类型语言(js/PHP)中, 当我们用==判断0和'0'以及空字符串(''或"")是否相等的时候, 返回的是true. 而且在PHP中, 当我们用==判断0和null是否相等的 ...
随机推荐
- AcWing1341. 十三号星期五
题目: 十三号星期五真的很不常见吗? 每个月的十三号是星期五的频率是否比一周中的其他几天低? 请编写一个程序,计算 N 年内每个月的 13 号是星期日,星期一,星期二,星期三,星期四,星期五和星期六的 ...
- SpringBoot 设置请求字符串格式为UTF-8
增加一个过滤器 package com.config; import com.jetsum.business.common.constant.CharsetConstant; import lombo ...
- 【LeetCode】90. Subsets II 解题报告(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 递归 回溯法 日期 题目地址:https://leet ...
- [LeetCode]485. Max Consecutive Ones 找到最大的连续的1的个数
题目描述 输入只有0和1的数组(长度为正整数,且<10000),找到最大的连续1的个数 比如[1,1,0,1,1,1],输出3 思路 遍历数组,统计当前连续个数curCount和最大连续值max ...
- 1030 - Discovering Gold
1030 - Discovering Gold PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 M ...
- C9软件工程非一线城市面试经验
本人C9软件工程毕业,由于家境一般,不想去一线城市面对天价房价,所以面的都不是互联网大厂. 人生第一面: 2021.11.29 五某汽车 软件工程岗面试 提前3天发了短信,然后拉了一个面试微信群 1. ...
- 「算法笔记」状压 DP
一.关于状压 dp 为了规避不确定性,我们将需要枚举的东西放入状态.当不确定性太多的时候,我们就需要将它们压进较少的维数内. 常见的状态: 天生二进制(开关.选与不选.是否出现--) 爆搜出状态,给它 ...
- 【计理01组03号】Java基础知识
简单数据类型的取值范围 byte:8 位,1 字节,最大数据存储量是 255,数值范围是 −128 ~ 127. short:16 位,2 字节,最大数据存储量是 65536,数值范围是 −32768 ...
- 解决windows update失败,正在还原的问题
其实这个不算问题,等上几个小时,还原完毕就好了,不过也有快速解决的办法. 所需工具:U盘.光盘等可以进入PE系统的工具,dism++软件 1.下载dism++工具,根据你的系统,选择使用32位还是64 ...
- CF786C Till I Collapse
题目分析 首先,对于这道题,可以用贪心以一个\(O(n)\)的复杂度求解一个\(k\)的值 暴力是\(O(n^2)\)的复杂度,当然过不了. 我们手推一下样例,会发现,答案满足单调性,于是,果断想到二 ...