C# 试题 03:可空类型与空值分析
0041 以下代码输出什么?
难度: 实战
int? a = null;
int? b = 5;
object o1 = a;
object o2 = b;
Console.WriteLine(o1 is null); // 1
Console.WriteLine(o2 is int); // 2
Console.WriteLine(o2 is int?); // 3
- A. 1、2 为
True,3 为False - B. 全部为
True(无值的可空值类型装箱为null;有值的装箱为底层值,is int?匹配非空可空值) - C. 1 为
False,2 为True - D. 3 编译失败
查看答案与解析
正确答案B
解析: 可空值类型装箱时:无值装箱为 null,有值装箱为底层值(int 而不是 Nullable<int>);类型模式 is int? 对非空的装箱底层值同样匹配,因此三行都是 True。
关键点: Nullable<T> 不是 CLR 类型,运行时只有“底层值”或“null”两种形态。
0042 以下代码输出什么?
难度: 实战
int? a = null;
int? b = 3;
Console.WriteLine(a + b); // 1
Console.WriteLine(a < b); // 2
- A. 第 1 行输出空行,第 2 行输出
False(提升算术结果为null;提升关系比较返回bool,任一为null时为false) - B. 两行都输出空行
- C. 两行都输出
False - D. 编译失败,
null不能参与运算
查看答案与解析
正确答案A
解析: 提升运算符中,算术(+、-、*、/)任一操作数为 null 结果为 null;关系(<、>、==)返回的是 bool 而非 bool?,任一为 null 时结果为 false。
关键点: 提升关系比较“为假”不等于“为空”,这也是 ?? 经常配合使用的原因。
0043 以下代码输出什么?
难度: 进阶
int? n = null;
int count = 0;
int result = n ?? ++count;
Console.WriteLine(result + "," + count);
- A.
1,1(??左侧为null时才求值右侧) - B.
0,0 - C.
1,0 - D.
0,1
查看答案与解析
正确答案A
解析: ?? 是短路运算符,左侧非 null 时右侧不执行;这里左侧为 null,执行 ++count,result 与 count 都是 1。
关键点: 把有副作用的表达式放在 ?? 右侧时要清楚它可能不执行。
0044 开启可空引用类型(NRT)后,以下代码会怎样?
难度: 进阶
static string? Maybe() => null;
string s = Maybe();
Console.WriteLine(s.Length);
- A. 编译错误
- B. 编译成功但有 CS8602 警告(可能为
null的解引用),运行时抛NullReferenceException - C. 编译成功且无警告
- D. 编译成功,运行时输出 0
查看答案与解析
正确答案B
解析: 把 string? 赋给 string 触发警告,解引用触发 CS8602;NRT 只是静态分析提示,运行时不生成判空代码,仍会抛 NullReferenceException。
关键点: 警告代表“编译器不确定”,不是运行时保护。
0045 以下代码运行结果是什么?
难度: 实战
string? s = null;
string t = s!;
Console.WriteLine(t.Length);
- A. 编译错误
- B. 编译有警告,运行正常
- C. 编译无警告,运行时抛
NullReferenceException - D. 编译无警告,运行输出 0
查看答案与解析
正确答案C
解析: !(空值抑制运算符)只告诉编译器“这里不会为 null”,从而消除警告;它不生成任何运行时检查,实际值为 null 时照样抛 NullReferenceException。
关键点: ! 是写给编译器看的,不是运行时转换。
0046 以下代码输出什么?
难度: 进阶
Console.WriteLine(default(int?) is null);
Console.WriteLine(default(string) is null);
Console.WriteLine(default(int) is null);
- A.
True、True、True - B.
True、True、False(int?与引用类型的默认值是null,int的默认值是 0) - C.
False、True、True - D.
False、False、False
查看答案与解析
正确答案B
解析: default(int?) 是“无值”的 Nullable<int>,is null 为 True;引用类型默认值就是 null;default(int) 是 0,不是 null。
关键点: default 对值类型与可空值类型含义不同。
0047 以下代码中哪一行能通过编译?
难度: 实战
bool? a = null;
bool? b = true;
var r1 = a & b; // 1
var r2 = a && b; // 2
- A. 第 1 行编译通过(结果为
null),第 2 行编译失败(bool?不支持短路&&) - B. 两行都编译,
r1和r2都是null - C. 两行都编译,
r1是false - D. 第 1 行编译失败,第 2 行编译通过
查看答案与解析
正确答案A
解析: &/| 提升到 bool? 后按三值逻辑计算(null & true 为 null);短路运算符 &&/|| 不支持 bool? 操作数,报 CS0019。
关键点: 需要三值逻辑用 &/|,但要注意结果仍是 bool?。
0048 以下代码运行结果是什么?
难度: 实战
object o = null;
int? n = (int?)o; // 1
int i = (int)o; // 2
- A. 两行都抛异常
- B. 两行都得到
null - C. 第 1 行
n为null不抛异常,第 2 行抛NullReferenceException - D. 第 1 行抛异常,第 2 行得到 0
查看答案与解析
正确答案C
解析: 把值为 null 的 object 强制转换为可空值类型得到“无值”的可空值,不抛异常;强转为非可空值类型 int 则抛 NullReferenceException。
关键点: 反序列化/反射场景中,(int?)obj 对 null 是安全的写法。
0049 关于 int? 与 string?,哪项说法正确?
难度: 进阶
- A. 两者都是值类型包装
- B.
int?是值类型Nullable<int>,有装箱、提升运算等运行时语义;string?只是编译期注解,运行时与string是同一个类型 - C. 两者都会改变运行时类型
- D.
string?是Nullable<string>的包装
查看答案与解析
正确答案B
解析: Nullable<T> 是真实的值类型,参与装箱、提升运算;可空引用类型注解只是编译期元数据,不影响运行时类型,也不会产生运行时检查。
关键点: 同名“可空”,一个是运行时机制,一个是静态分析约定。
0050 关于 x == null 与 x is null,哪项正确?
难度: 进阶
- A. 两者完全等价
- B.
is null也会调用重载的== - C.
is null是模式匹配,不参与运算符重载决议;==可能被重载,对自定义类型行为可能不同 - D.
x == null永远不会被重载
查看答案与解析
正确答案C
解析: == 是运算符,可被类型重载(如 string、decimal 或自定义类型);is null 是模式匹配,直接做空判断,不触发重载。
关键点: 对可能重载 == 的类型,优先用 is null 表达“真的是 null”。
0051 以下代码输出什么?
难度: 实战
int? a = null;
a++;
int? b = 3;
b++;
Console.WriteLine(a);
Console.WriteLine(b);
- A. 两行都输出空行
- B. 输出
0和4 - C. 编译失败
- D. 第一行输出空行(
null自增后仍是null),第二行输出4
查看答案与解析
正确答案D
解析: 提升的 ++:可空值为 null 时保持 null,有值时对底层值自增。a 输出空行,b 输出 4。
关键点: 对可空值类型自增前先判断是否有值。
0052 以下代码输出什么?
难度: 进阶
int? n = null;
string s = null;
Console.WriteLine("n=" + n + ", s=" + s);
- A.
n=, s= - B.
n=null, s=null - C. 编译失败
- D.
n=0, s=
查看答案与解析
正确答案A
解析: 字符串拼接对 null 按空串处理:"n=" + null 得 "n=",s 同理,结果 n=, s=。
关键点: 拼接或插值时 null 输出为空,而不是字符串 "null"。
0053 开启 NRT 后,以下代码会怎样?
难度: 进阶
class Order
{
public string Customer { get; set; }
}
- A. 编译错误
- B. 编译成功但有 CS8618 警告(非空属性未在构造函数中初始化)
- C. 编译成功且无警告
- D. 编译成功,运行时自动初始化为空串
查看答案与解析
正确答案B
解析: 非空引用类型属性在构造函数中未初始化会触发 CS8618;实际运行时该属性默认仍是 null,读取可能得到 null。
关键点: 警告提示“契约可能被破坏”,不是运行时默认值机制。
0054 以下代码能否通过编译?
难度: 实战
static void M<T>(T value) where T : struct { }
M<int?>(null);
- A. 编译成功
- B. 运行抛异常
- C. 编译失败,
Nullable<int>不满足struct约束 - D. 编译成功,但参数永远是
null
查看答案与解析
正确答案C
解析: where T : struct 约束不接受可空值类型作为类型实参:int? 本身不满足该约束,编译报错。
关键点: 约束 T : struct 与 T : notnull/unmanaged 对可空值类型的处理不同。
0055 关于可空引用类型的编译期警告,哪项正确?
难度: 进阶
- A. 警告在运行时会被强制检查
- B. 警告只是编译期提示,运行时不产生任何检查,仍可能
NullReferenceException - C. 警告会阻止编译
- D. 开启 NRT 后所有解引用都会自动判空
查看答案与解析
正确答案B
解析: NRT 的警告是纯静态分析产物,不生成运行时判空代码;该抛 NullReferenceException 的地方照常抛。
关键点: 把 NRT 当“契约标注”,不要当“运行时保护”。
0056 以下代码为什么需要 ??=?
难度: 实战
var map = new Dictionary<string, List<int>>();
map.TryGetValue("k", out var list);
list ??= new List<int>();
list.Add(1);
- A.
TryGetValue的out参数带[MaybeNullWhen(false)],查找失败时是null;??=只在为null时创建 - B. 不需要
??=,out参数失败时自动是空集合 - C.
??=在这里会把list置为null - D. 第 2 行已经抛异常,不会执行到
??=
查看答案与解析
正确答案A
解析: TryGetValue 失败时把 out 置为 null,配合 [MaybeNullWhen(false)] 注解,编译器把它视为可空;list ??= new List<int>() 只在为 null 时新建,是“取或建”的标准写法。
关键点: 可空分析让 out 的 null 风险显式化,??= 是推荐兜底。
0057 以下代码输出什么?
难度: 进阶
List<int>? list = null;
int? count = list?.Count;
Console.WriteLine(count ?? -1);
- A.
0 - B.
null - C.
-1(?.使结果类型变成int?,为null时??生效) - D. 编译失败
查看答案与解析
正确答案C
解析: 空条件运算符会让表达式结果提升为可空类型:list?.Count 的类型是 int?,list 为 null 时结果是 null,?? -1 输出 -1。
关键点: ?. 的结果可能“吃掉”原来的非空类型,注意与 ?? 配合。
0058 以下代码输出什么?
难度: 实战
static T? GetDefault<T>() => default;
Console.WriteLine(GetDefault<int>() == null);
Console.WriteLine(GetDefault<string>() is null);
- A.
False和True(无约束时T?表示“可 default”:值类型结果是 0,引用类型才是null) - B.
True和True - C.
False和False - D. 编译失败
查看答案与解析
正确答案A
解析: C# 9 起无约束泛型的 T? 表示“可 default”并反映在注解上:T 为值类型时 default 是 0(== null 为 False),引用类型时是 null。
关键点: 无约束 T? 与 int?/string? 语义都不同,最容易在泛型工具方法里踩坑。
0059 以下代码运行结果是什么?
难度: 进阶
int? n = null;
try
{
Console.WriteLine(n.Value);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name);
}
- A.
0 - B.
null - C.
NullReferenceException - D.
InvalidOperationException(无值可空值类型访问Value抛该异常)
查看答案与解析
正确答案D
解析: 无值可空值类型访问 .Value 抛 InvalidOperationException;判空应使用 HasValue、TryGetValue 模式或 ??。
关键点: .Value 是“我确定有值”的强断言,出错类型与 NullReferenceException 不同。
0060 以下代码输出什么?
难度: 进阶
int? n = null;
string s = null;
Console.WriteLine($"n={n}, s={s}");
- A.
n=, s=(null在插值中格式化为空) - B.
n=null, s=null - C. 编译失败
- D.
n=0, s=null
查看答案与解析
正确答案A
解析: 字符串插值对 null 值格式化为空字符串(内部走 IFormattable/ToString 对 null 的空处理),结果 n=, s=。
关键点: 与 0052 的拼接行为一致:null 插值为空。