CodeGym /课程 /C# SELF /编译器警告分析

编译器警告分析

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

1. 入门

想象一下,代码就像一场戏,变量就是演员。每次变量进入新场景(代码块),它可能是“活着的”(有值)或者“幽灵”(null)。但问题来了:观众——编译器——不是现场看戏,而是看剧本。他能看到演员啥时候被分配角色(赋值),啥时候消失(变成null)。但他没法看到现实(程序运行),只能根据剧本猜!

静态数据流分析,就是编译器在程序运行前检查代码,看看会不会有人演出“NullReferenceException崩溃”的戏码。

过程本质

从C# 8.0和Nullable Reference Types(NRT)模式开始,编译器会分析变量在某些执行分支里会不会变成null。如果有可能——它立马告诉你。它不会让你“用一个突然变成幽灵的演员的名字”。

2. 怎么看懂编译器警告

主要警告类型

NRT模式开着的时候,编译器会特别关注引用类型变量(string、User、数组、对象)。最常见的警告有这些:

  • Possible null assignment —— 你试图把可能是null的值赋给不允许null的变量。
  • Dereference of a possibly null reference —— 你访问对象成员,但编译器怀疑它可能是null。
  • Possible null return —— 方法可能返回null,但签名要求必须有值。
  • Nullable object must have a value —— 你在nullable类型上用.Value,但没检查。
  • Unassigned non-nullable property —— 你没给不允许null的字段/属性赋值。

一般在IDE(比如Rider或Visual Studio)里,这些警告会用波浪线标出来,鼠标悬停会有解释。

典型警告示例


#nullable enable

string? possibleNull = GetString();

// 哎呀,编译器警告:"Dereference of a possibly null reference"
int length = possibleNull.Length; // 警告!

编译器不确定possibleNull是不是null,所以访问.Length可能会抛异常。

如果方法长这样:

string? GetString() { ... }

你写:

string nonNullable = GetString();

就会收到警告:可能把null不安全地传给non-nullable变量。

3. 编译器分析你的代码

例1:直接赋值


string? name = null;
Console.WriteLine(name.Length); // 警告:可能访问null

这里很明显:name是null,所以访问属性有风险。

例2:判断null


string? name = GetUserName();

if (name != null)
{
    Console.WriteLine(name.Length); // 没问题!
}
else
{
    Console.WriteLine("名字没填!");
}

编译器知道在if (name != null)块里变量是安全的。这就是flow analysis——根据条件分析值的流向。

4. 编译器没法“看进方法里”

它只看签名——如果写着方法返回string?,它就信可能是null,哪怕里面总是返回字符串。

例3:方法返回值


string? GetMaybeName(bool useName)
{
    if (useName)
        return "Code Jedi";
    else
        return null;
}

void PrintLength()
{
    string? name = GetMaybeName(false);

    Console.WriteLine(name.Length); // 警告!
}

编译器看到GetMaybeName可能返回null,所以让你小心点。

5. 常见场景和警告

把nullable赋给non-nullable变量


string? maybeUser = GetUser();
string alwaysUser = maybeUser; // warning: possible null assignment

要消除警告:


string alwaysUser = maybeUser ?? "游客";

这样总会有字符串——要么来自方法,要么是“游客”。

忘了检查.HasValue的nullable值类型


int? age = GetAge();
int realAge = age.Value; // warning: 可能访问null

要么这样:


if (age.HasValue)
    Console.WriteLine(age.Value);

或者:


int realAge = age ?? -1;

自动属性没初始化


class User
{
    public string Name { get; set; } // warning: 没初始化!
}

解决方法:


public string Name { get; set; } = "无名";

或者


public string? Name { get; set; }

6. 数据流分析的复杂情况

循环里的分析


string? text = null;

while (text == null)
{
    text = Console.ReadLine();
}

Console.WriteLine(text.Length); // 没问题:编译器懂了!

多种赋值路径


string? name;
if (Random.Shared.Next() % 2 == 0)
    name = "Alice";
else
    name = null;

Console.WriteLine(name.Length); // warning: possible null dereference

编译器会分析所有分支。只要有一个是null,它就会警告。

7. 怎么“安抚”编译器(以及什么时候别这么做)

有时候你很确定值不会是null,但编译器不信:


string? value = GetSomething();
if (value == null)
    throw new Exception("需要有值!");

Console.WriteLine(value.Length); // 警告没了

或者你用!操作符:


string? value = GetSomething();
Console.WriteLine(value!.Length); // 没警告,但有风险

这叫null-forgiving operator。它啥都不检查——只是告诉编译器:“别担心,哥稳得很”。但你要是搞错了,运行时就崩。

2
任务
C# SELF, 第 14 级, 课程 4
已锁定
在使用变量之前检查是否为 null
在使用变量之前检查是否为 null
2
任务
C# SELF, 第 14 级, 课程 4
已锁定
使用 null 合并运算符消除警告
使用 null 合并运算符消除警告
1
调查/小测验
Nullable类型第 14 级,课程 4
不可用
Nullable类型
Nullable Reference Types入门
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION