.NET项目升级:可为空引用
c# 8引入了新特性:“可为空引用”(),这个功能个人觉得挺好的,能够非常明确的表现程序设计者的意图,编译器能够进行检查,尽最大可能减小nullreferenceexception错误。
如果是新项目,那么上手很简单,一点点搭建起来,遇山开山,遇河渡河。但是对于我这种手头上的项目大多都是以前创建的情况,就要稍微做那边么一点操作了。
要看完整说明,请查看开头的那个链接。
准备
首先评估一下几个条件:
- 项目可以基于.net core 3.0及以上编译。如果不行,那么就请直接右上角点×。
- 是不是大多数的变量都需要null引用?如果是的话,个人觉得不值得费劲了。
操作
以一个asp.net webapi为例,项目修改前是能够正常编译无错误无警告的。
1. 启用nullable(可为空引用类型)
nullable默认是不启用的,需要做一些修改以启用。有两种方式:
- 修改csproj文件,在properygroup里面添加
enable 项。
对于比较小型的项目,可以直接修改,这样弹出来的警告或者错误会比较少,方便我们快速改正。
- 使用编译器指令#nullable enable和#nullable restore进行修改。在代码段的开头enable,结尾处restore。
对于中大型项目,直接使用第一种方式进行修改会导致大量的警告,很容易一团糟;可以通过编译器指令对单文件或者单类进行修改操作,一点一点地修改。
2. 修改代码
我的项目使用第一种方法的的情况下有24个警告(编译后有67个),也不知道算多还是算少。
实体类
[datacontract] [table("recordinfo")] public class recordinfo : infobase { /// <summary> /// 记录id /// </summary> [datamember] [key] public string recordnum { get; set; } /// <summary> /// 车辆rfid号码 /// </summary> [datamember] public string carid { get; set; }
recordnum为主键,通过ef进行映射,结果也不会为null,所以声明应该保持原样即可。carid不是主键,有可能是null,因此应当显式声明为string?,表示可以为空,删除警告。
编译器检查,recordnum没有被初始化,我们的设计意图告诉编译器了,但是代码还没有保证这个不能为空,因此需要修改代码保证recordnum不为空。
这里使用null包容运算符(!)来进行操作,提示编译器这个位置实际上不会为null。
//string的default为null,通过增加!告诉编译器,这块初始化的时候实际上是不为空的。 public string recordnum { get; set; } = default!;
null包容运算符并不能确保不是null,如果可以使用代码确保不为null,那么使用代码会是更优选择。考虑如下代码:
//我经常使用string.isnullorwhitespace来进行检查,空文本对我的业务没有意义,因此适用。 public string recordnum { get; set; } = "";
特别提示:
可为空引用类型检查是编译器的行为,它可以提供编译时检查,但是不提供运行时检查,如果使用外部代码调用,那么是否为空都可以进行赋值。
很明显,上面代码运行时也很难保证不是null,我们可以再改进一下。
public string recordnum { get => recordnum; set => recordnum = value ?? ""; } private string recordnum = "";
官方推荐对poco类使用构造函数保证不为空。
指定了default!的情况,asp.net core webapi会内部自动标注[required],远程调用如果缺失参数,会提示bad request。
datacontext类
datacontext也是类似的,主要是dbset对象的引用问题。
来自.net class library
//basedirectory的返回是string?类型的 var basedirectory = system.appdomain.currentdomain.basedirectory; //path.combine()不接受string?,提示错误。 var xmlpath = path.combine(basedirectory, system.appdomain.currentdomain.friendlyname + ".xml");
这是一个潜在的bug点,对于以上代码,很显然basedirectory的返回为null不符合我们的设计,我们可以进行如下改造。
var basedirectory = system.appdomain.currentdomain.basedirectory; if (basedirectory == null) throw new argumentnullexception("basedirectory"); var xmlpath = path.combine(basedirectory, system.appdomain.currentdomain.friendlyname + ".xml");
泛型类
public class returndata<t> { //整个类型会提示data未能初始化,errormsg未能初始化。 public returndata(){ } public returndata(t data) => data = data; public returndata(string error) => errormsg = error; /// <summary> /// 页面数据 /// </summary> public t data { get; set; } public string errormsg { get; set; } }
设计意图:data与errormsg不同时为空,也不同时有值。
基于设计,可以做如下修改。注意添加了class约束。
public class returndata<t> where t: class { public returndata(){ } public returndata(t data) => data = data; public returndata(string error) => errormsg = error; /// <summary> /// 页面数据 /// </summary> public t? data { get; set; } public string? errormsg { get; set; } }
其他例子
using managedatacontext context = new managedatacontext(); var props = contexttype.getproperty($"{namestring}s"); //props提示有可能为null var dbset = (props.getvalue(context) as dbset<t>); //提示dbset可能为null var res = await dbset.findasync(value);
可以调整为下面的形式:
using managedatacontext context = new managedatacontext(); var props = contexttype.getproperty($"{namestring}s"); //判断props可以解决问题。 if (props == null) throw new argumentnullexception("props"); var dbset = (props.getvalue(context) as dbset<t>); //判断dbset可以解决问题。 if (dbset == null) throw new argumentnullexception("dbset"); var res = await dbset.findasync(value);
注意,将as替换为强制转换,并不能消除警告。
总结
最后消除了所有的警告,改造结束。
这个新的语言特性可以帮助我们发现一些潜在的bug点,帮助我们养成良好的编程习惯,也便于我们告诉其他人我们的设计意图。
编译器能帮我们做的工作,就没必要自己再费劲做了,懒的不行,我得歇会儿。
推荐阅读
-
WinForms项目升级.Net Core 3.0之后,没有WinForm设计器?
-
在Mac上开发使用yoeman构建Asp.net core项目并且实现分层引用
-
.NET项目升级:可为空引用
-
VS2017怎么创建创建空的ASP.NET Core Web项目?
-
Abp vNext框架 从空项目开始 使用ASP.NET Core Web Application-笔记
-
asp.net mvc 4 项目升级到 asp.net mvc5
-
WinForms项目升级.Net Core 3.0之后,没有WinForm设计器?
-
ASP.NET Core 实战:将 .NET Core 2.0 项目升级到 .NET Core 2.1
-
.net软件工程师请问zend studio实现三层架构,怎么新建项目以及引用
-
.net软件工程师请问zend studio实现三层架构,怎么新建项目以及引用