CodeGym /课程 /C# SELF /和null打交道:操作符 ??, ?., ?, !, default

和null打交道:操作符 ??, ?., ?, !, default

C# SELF
第 14 级 , 课程 3
可用

1. null合并操作符(??

你要是不会写简洁又安全的nullable类型代码,那你对nullable类型的理解就还不完整。在C#里有两个超好用的操作符:null合并(??null条件操作符(?.

??操作符可以给可能是null的变量设置一个“默认值”。

想象一下,你有个用户名,可能是null。如果真的是null,你想显示“游客”。比如:

string userName = null;
string displayName = userName != null ? userName : "游客";
经典的 null判断

??操作符可以写得更短:

string userName = null;
string displayName = userName ?? "游客";
??设置默认值

怎么用:

  • 如果左边不是null,就直接返回左边的值。
  • 如果左边是null,就用右边的值。

再举几个例子:

int? age = null;
int displayAge = age ?? -1; // -1 —— 默认值
Console.WriteLine(displayAge); // 输出-1

string input = null;
string name = input ?? "游客";
Console.WriteLine($"你好, {name}!"); // 你好, 游客!

操作符链式用法

??可以连着用:

string result = str1 ?? str2 ?? "默认值";
??操作符链

如果str1不是null,就用它。否则用str2,如果它也是null,那就用"默认值"。

2. null条件操作符(?.

还有一种常见情况——你只想在对象不是null的时候调用方法或者访问属性。比如:

User user = null;
string displayName = user != null ? user.Name : null;
访问属性前的经典 null判断

?.操作符可以更简单:

User user = null;
string displayName = user?.Name;
null条件操作符 ?.

如果user对象是null,表达式不会报错,只会返回null

举几个例子:

User user = null;

// 没有?. —— 会报错
// Console.WriteLine(user.Name); // NullReferenceException

Console.WriteLine(user?.Name); // 安全——会打印空字符串或者啥都不打印

Console.WriteLine(user?.GetProfileInfo()); // 也是一样

User[] users = null;
int? count = users?.Length; // 如果users == null,count就是null

操作符链式用法

你可以写一整条“链”:


string domain = company?.Director?.Email?.Split('@')?[1];
?.链安全访问嵌套属性

如果链路上任何一个是null,表达式就直接返回null,不会抛异常。

??一起用

?.??配合起来超好用:


string display = user?.Name ?? "未知";
安全输出+默认值

如果user或者user.Namenull,就会输出“未知”。

3. !是啥,有啥用

抑制null警告的操作符

在新版C#(开了NRT模式)里,编译器会很贴心地提醒你:你在用一个可能是null的变量。但有时候你自己很确定没问题,这时候就可以用抑制操作符!


string? possibleNull = GetUserNameMaybeNull();
Console.WriteLine(possibleNull.Length); // 警告:可能是null

Console.WriteLine(possibleNull!.Length); // 编译器不吭声,但如果真是null——直接异常

注意:!不会帮你防错——它只是告诉编译器“相信我”。如果变量真的是null,就会抛NullReferenceException

啥时候别用它

别瞎用!。好习惯是尽量少用它。最好把代码写得null要么不可能出现,要么明确处理掉。

4. default——怎么拿到类型的“标准”值

有时候你就是想把变量“重置”到初始状态,尤其是不想手动写0false或者null的时候。

这时候就用关键字default

int a = default;            // a == 0
bool flag = default;        // flag == false
string s = default;         // s == null
double? d = default;        // d == null
default重置变量

在你的小程序里,比如可以重置名字或年龄:

userName = default; // null
userAge = default;  // null(如果userAge是int?)

5. 区别和细节:?!default

就算是老司机,有时候看到?!default挨着也会懵。咱们来理理谁是谁。

?——你允许null

  • 值类型:int? x —— 现在x可以是null
  • 引用类型:string? s —— 明确告诉编译器变量可以是null(NRT模式下)。

!——你保证不会有null

  • user!.Name —— 你向编译器承诺user绝对不是null
  • 只在Nullable Reference Types模式下有用。

default——你要“默认值”

  • int x = default; —— x会变成0
  • string s = default; —— s会变成null

对比一下:

语法 这是什么? 哪里能用? null啥关系?
int?
Nullable值类型 到处都能用 可以赋值null
string?
Nullable引用类型(NRT) NRT模式下 可以赋值null
!
Null-forgiving操作符 NRT模式下 抑制警告
default
默认值 到处都能用 引用类型就是null

6. 用?!default时的常见坑

坑1:以为!能“治愈”null。
其实!只是让编译器别唠叨。如果值真的是null,程序还是会挂掉,抛NullReferenceException

坑2:用.Value前忘了判断.HasValue
尤其是int?bool?这些nullable类型。不判断直接用可能会抛InvalidOperationException

坑3:在需要“特殊”空值语义时用default
比如0false可能是合法值,不是“空”的意思。这样会导致逻辑bug。

坑4:在NRT模式下要么忘了给引用类型加?,要么乱加。
有人忘了加?,编译器就一堆警告。有人反过来,哪儿都加?,其实null根本不会出现。两种都让代码难读又不靠谱。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION