C# 使用于变量尾部的空包容运算符!,三元条件运算符?:

1,089 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情

主要介绍一下在可空类型变量使用过程中,最常遇到的取非空值的情况,由于兼容性的考虑,直接使用可空类型目前只有警告,如果严格一些,只有 可空类型变量 不为空的值才能使用。而 空抑制运算符 可以直接获取非空值,在 明确变量不为空、或者不能使用null值 的情况下,非常有用。

此外,还稍微介绍下,可能是编程语言中非常常见的三元条件运算符 condition?express1:express2,同时也是极少达到三元的操作符。

! 空包容或空抑制运算符 null-forgiving operator/null-suppression operator

!是一个一元后缀运算符,位于变量的后面。其实称之为 空抑制运算符 含义更加贴切。!用于获取一个可空类型的非空,或者 将可空类型转换为非空。

x! 声明 可为空的引用类型的表达式 x 不为 null

空包容运算符在运行时不起作用,它仅通过更改表达式的 null 状态来影响编译器的静态流分析。在运行时其结果为 x 的结果。

一元前缀!运算符 是 逻辑非运算符。

使用空抑制运算符,将一个可空类型赋值为不为空的类型:

string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness

比如一个最简单的使用:

Test MyTest2()
{
    Test? a = new Test();
    //Test? a = null ;

    var b = a ;

    var c = a!;

    return a!;
}

空包容运算符 可以用来测试参数的验证逻辑。比如下面的类:

#nullable enable
public class Person
{
    public Person(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));

    public string Name { get; }
}

#nullable enable 预处理指令 用于 启动可空类型。

在测试中,为了验证参数,可以如下处理:

[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void NullNameShouldThrowTest()
{
    var person = new Person(null!);
}

启用可空类型的情况下,是不允许将 null 传递给 string 类型的,需要为 string? 可空字符串才行。将会产生警告:Warning CS8625: Cannot convert null literal to non-nullable reference type

如果不使用 空包容运算符 ,可以看到如下的 CS8625 警告:

通过使用 空包容运算符,告知编译器传递 null 是预期行为,不应发出警告。

在明确知道某个表达式不为 null 时,也可以使用 空包容运算符

比如,下面 IsValid 方法返回true,则说明其参数不是 null,则可以取消 null 类型引用:

public void NullTest()
{
    Person? p = new Person("John");
    if (IsValid(p))
    {
        Debug.WriteLine($"Found {p!.Name}");
    }
}

public static bool IsValid(Person? person)
=> person is not null && person.Name is not null;

如果不使用 null包容运算符,编译器将为 p.Name 生成警告:Warning CS8602: Dereference of a possibly null reference

IsValid 方法中,可以使用 NotNullWhen 特性,当方法返回 true 时,方法的参数不能是 null:

public static bool IsValid2([NotNullWhen(true)] Person? person)
=> person is not null && person.Name is not null;

?: 条件运算符conditional operator 或 三元条件运算符

三元条件运算符的语法如下:当条件为真true时,取值consequent,否则(false时)取值alternative

condition ? consequent : alternative

C# 9.0 之前,条件表达式中两个可能取值的类型必须相同

var rand = new Random();

int? x = (rand.NextDouble() > 0.5) ? 12 : 0;

IEnumerable<int> xs = x ==0 ? new int[] { 0, 1 } : new int[] { 2, 3 };

从 C# 9.0 开始,两个取值的类型可以相同,也可以是能够隐式转换的类型。

var rand = new Random();

int? x = (rand.NextDouble() > 0.5) ? 12 : null;

IEnumerable<int> xs = x is null ? new List<int>() { 0, 1 } : new int[] { 2, 3 };

参考