.NET项目升级手记:可为空引用

简介:

.NET项目升级手记:可为空引用

c# 8引入了新特性:“可为空引用”(详情),这个功能个人觉得挺好的,能够非常明确的表现程序设计者的意图,编译器能够进行检查,尽最大可能减小NullReferenceException错误。

如果是新项目,那么上手很简单,一点点搭建起来,遇山开山,遇河渡河。但是对于我这种手头上的项目大多都是以前创建的情况,就要稍微做那边么一点操作了。

要看完整说明,请查看开头的那个链接。

准备
首先评估一下几个条件:

项目可以基于.NET CORE 3.0及以上编译。如果不行,那么就请直接右上角点×。
是不是大多数的变量都需要null引用?如果是的话,个人觉得不值得费劲了。
操作
以一个ASP.NET WEBAPI为例,项目修改前是能够正常编译无错误无警告的。

  1. 启用Nullable(可为空引用类型)
    Nullable默认是不启用的,需要做一些修改以启用。有两种方式:

修改csproj文件,在ProperyGroup里面添加enable项。
对于比较小型的项目,可以直接修改,这样弹出来的警告或者错误会比较少,方便我们快速改正。

使用编译器指令#nullable enable和#nullable restore进行修改。在代码段的开头enable,结尾处restore。
对于中大型项目,直接使用第一种方式进行修改会导致大量的警告,很容易一团糟;可以通过编译器指令对单文件或者单类进行修改操作,一点一点地修改。

  1. 修改代码
    我的项目使用第一种方法的的情况下有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类使用构造函数保证不为空。

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
{

//整个类型会提示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

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);
//提示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);
//判断dbset可以解决问题。
if (dbset == null) throw new ArgumentNullException("dbset");
var res = await dbset.FindAsync(value);
注意,将as替换为强制转换,并不能消除警告。

总结
最后消除了所有的警告,改造结束。

这个新的语言特性可以帮助我们发现一些潜在的bug点,帮助我们养成良好的编程习惯,也便于我们告诉其他人我们的设计意图。

编译器能帮我们做的工作,就没必要自己再费劲做了,懒的不行,我得歇会儿。

原文地址https://www.cnblogs.com/podolski/p/12692888.html

相关文章
|
2月前
|
数据可视化 网络协议 C#
C#/.NET/.NET Core优秀项目和框架2024年3月简报
公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:追逐时光者,第一时间获取每周精选分享资讯🔔)。
120 1
|
2月前
|
存储 开发框架 NoSQL
ASP.NET WEB——项目中Cookie与Session的用法
ASP.NET WEB——项目中Cookie与Session的用法
58 0
|
2月前
|
开发框架 前端开发 .NET
ASP.NET WEB——项目创建与文件上传操作
ASP.NET WEB——项目创建与文件上传操作
63 0
|
4天前
|
开发框架 .NET API
.NET Core 和 .NET 标准类库项目类型有什么区别?
在 Visual Studio 中,可创建三种类库:.NET Framework、.NET Standard 和 .NET Core。.NET Standard 是规范,确保跨.NET实现的API一致性,适用于代码共享。.NET Framework 用于特定技术,如旧版支持。.NET Core 库允许访问更多API但限制兼容性。选择取决于兼容性和所需API:需要广泛兼容性时用.NET Standard,需要更多API时用.NET Core。.NET Standard 替代了 PCL,促进多平台共享代码。
|
1月前
|
开发框架 .NET Linux
【.NET Developer】已发布好的.NET Core项目文件如何打包为Docker镜像文件
该文介绍了如何不使用VS2019手动创建ASP.NET Core Blazor项目的Dockerfile并构建Docker镜像。首先,创建名为Dockerfile的文件,并复制提供的Dockerfile内容,该文件指定了基础镜像和工作目录。然后,通过CMD在项目目录下运行`docker build -t 自定义镜像名 .`来生成镜像。最后,使用`docker run`命令启动容器并验证项目运行。此外,文章还提到了将镜像推送到Azure Container Registry (ACR)的步骤。
|
1月前
|
Linux C# C++
【.NET Developer】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
本文介绍了如何使用VS2019和.NET框架创建一个Blazor应用,并将其部署到Azure应用服务。首先,Blazor是一个使用C#而非JavaScript构建交互式Web UI的框架,支持共享服务器和客户端应用逻辑,以及与Docker和Azure集成。任务包括创建Blazor项目,配置Dockerfile为Linux容器,本地测试,发布到Azure Container Registry (ACR),然后在Azure App Service for Container上部署。在部署过程中,需确保Docker设置正确,开启ACR的Admin访问权限,并监控镜像拉取和容器启动日志。
|
1月前
|
人工智能 自然语言处理 算法
分享几个.NET开源的AI和LLM相关项目框架
分享几个.NET开源的AI和LLM相关项目框架
|
2月前
|
人工智能 自然语言处理 算法
分享几个.NET开源的AI和LLM相关项目框架
分享几个.NET开源的AI和LLM相关项目框架
|
2月前
|
算法 BI API
C#/.NET/.NET Core优秀项目和框架2024年1月简报
C#/.NET/.NET Core优秀项目和框架2024年1月简报
123 2
|
2月前
|
SQL 开发框架 前端开发
ASP.NET WEB项目中GridView与Repeater数据绑定控件的用法
ASP.NET WEB项目中GridView与Repeater数据绑定控件的用法
51 0