作为C#程序员,肯定对“未将对象引用设置到对象的实例”这样的报错很熟悉,每个人(不是几乎)都肯定遇到过这个异常,而很多时候,解决方案也非常简单,在访问这个对象之前加一个“是否为空”的检查,类似
invoice.payment() //忘记检查invoice是否为空了
//解决方案也就是在访问成员前加上检查
if(invoice == null) {
//notice, exception, return, etc
}
invoice.payment()
Null的两种语义
出现null
通常有两种情况:
- 函数签名规定了返回值,但是没有成功计算出这个返回值,我们想通过返回
null
告诉调用者,我做不到。所以也是期盼调用者来检查。 - 本来是想返回一个值类型,比如
decimal CaculatePrice()
函数签名要求返回的是值类型,但是计算中出错了,比如调用第三方API出错了,返回任何值都不是非常合适,那么我就想明确返回一个null
来告诉调用者,出异常
了。通常做法也是使用Nullable<T>
来修改签名为decimal? CaculatePrice()
对于第二种情况,正确的做法是使用异常(Exception),这也正是BCL常见的做法,比如在看MSDN上的某个函数文档的时候你会发现专门有一节就是Exceptions,用来列出可能会出现的异常,比如下面是WebClient.DownloadString
的Exceptions文档内存:
这个表示当你调用DownloadString
方法的时候你需要显示检查着三种异常。但是异常是很重的装备,没有多少人会想自己手动这么细致的处理异常,也就是导致了:要么我们不检查异常,要么我们try-catch-all
来捕获所有的异常。
值类型和引用类型
- 值类型有默认值,比如
int
的默认值是0
- 值类型可以通过
Nullable<T>
来修饰可空值类型,这样可以设置为null
,比如int?
- 引用类型默认是
null
CSharp 8.0 可空值类型(Nullable Reference Type)
在C#的8.0版本中设计者统一了值类型和引用类型的可空处理:
- 值类型,默认不能为
null
,可以通过?
修饰类变为可空类型。 - 引用类型,默认不能为
null
,可以通过?
修饰类变为可空类型。
这相当于一个Breaking changes,所以默认的Visual Studio中的C#静态分析器会将可能是引用类型可能为null
的标记为warning
,可以在设置中打开和关闭这样的warning
。
最终达到的目标就是你在程序中检查了可能为null的地方,直到你显示(explict)地标记一个引用类型可能为null
。相当于说:编译器和分析器你别管了,风险我来担。而不是“偷偷”地将这些风险推延到运行时触发:未将对象引用设置到对象的实例。
Comments: