C# 试题 01:变量、常量与类型推断
0001 以下代码中 x 的类型是什么?
难度: 进阶
var x = flag ? 1 : 2.0;
Console.WriteLine(x.GetType());
- A.
int - B.
double,条件表达式的两个分支统一为double - C.
long - D. 编译失败,
var无法推断
查看答案与解析
正确答案B
解析: 条件表达式要求两个分支可统一到同一类型,int 可隐式转换为 double,因此结果为 double;即使 flag 为真,运行时也是装箱为 double 的值。
关键点: 类型统一发生在编译期,var 拿到的是统一后的类型。
0002 以下哪些声明能通过编译?
难度: 进阶
var a = default(int); // 1
var b = default; // 2
var c = null; // 3
var d = (object)null; // 4
- A. 只有第 1 项
- B. 第 1 项和第 4 项
- C. 全部
- D. 只有第 2 项
查看答案与解析
正确答案B
解析: default(int) 有明确类型,(object)null 也有目标类型;default 字面量和 null 都没有可供 var 推断的具体类型,编译失败。
关键点: var 需要初始化表达式提供唯一静态类型。
0003 以下代码输出什么?
难度: 进阶
class Config
{
public static int a = b + 1;
public static int b = 10;
}
Console.WriteLine($"{Config.a},{Config.b}");
- A.
1,10,静态字段按文本顺序初始化,a初始化时b还是 0 - B.
10,10 - C.
11,10 - D. 编译失败
查看答案与解析
正确答案A
解析: 静态字段初始化按声明顺序执行:a = b + 1 先执行,此时 b 仍为默认值 0,所以 a = 1;随后 b = 10。
关键点: 静态初始化顺序是“文本顺序”,不要依赖未初始化字段。
0004 关于 var 与 dynamic,哪项说法正确?
难度: 进阶
- A.
var在编译期完成类型决议并参与重载决议;dynamic在运行时决议,依赖静态决议的特性(如扩展方法)无法直接用于dynamic - B. 两者等价,只是写法不同
- C.
dynamic在编译期做完整类型检查 - D.
var是运行时类型,dynamic是编译期类型
查看答案与解析
正确答案A
解析: var 编译后与显式类型无异,正常参与重载决议和扩展方法查找;dynamic 的所有成员访问在运行时绑定,扩展方法这类静态机制不会参与,调用不存在的方法会抛 RuntimeBinderException。
关键点: dynamic 是有意放弃编译期检查,不是 var 的等价物。
0005 以下代码能否通过编译?
难度: 进阶
for (int i = 0; i < 3; i++) { }
for (int i = 0; i < 3; i++) { }
int i = 10;
- A. 不能,
i重复声明 - B. 能,循环变量的作用域限定在各自的
for语句内,与后面的i互不冲突 - C. 不能,第三个
i与前面冲突 - D. 只有第一个循环能编译
查看答案与解析
正确答案B
解析: for 头部声明的变量作用域是整个 for 语句,两个循环和后续声明处于互不重叠的作用域,全部合法。
关键点: 作用域按声明位置计算,而不是按方法整体计算。
0006 目标类型 new(C# 9+)中,哪一行无法编译?
难度: 进阶
Dictionary<string, List<int>> map = new() // 1
{
["a"] = new() // 2
};
List<int>? maybe = new(); // 3
var x = new(); // 4
- A. 第 1 行
- B. 第 2 行
- C. 第 4 行,
var不提供目标类型 - D. 第 3 行,可空引用类型注解下
new()不可用
查看答案与解析
正确答案C
解析: 第 1、2 行分别以 Dictionary<...> 和 List<int> 为目标类型,第 3 行目标类型是 List<int>(可空注解不改变底层类型);var x = new() 没有目标类型,编译失败。
关键点: 目标类型 new 只认“底层类型”,可空注解不影响。
0007 以下代码哪一项能通过编译?
难度: 实战
const int A = int.MaxValue + 1; // 1
const int B = unchecked(int.MaxValue + 1); // 2
- A. 第 1 项
- B. 第 2 项,常量表达式默认按 checked 检查,
unchecked允许溢出回绕为int.MinValue - C. 两项都能编译
- D. 两项都不能编译
查看答案与解析
正确答案B
解析: 常量表达式即使在不开启全局 checked 的项目里也默认按 checked 语义求值,int.MaxValue + 1 报 CS0220;包在 unchecked 中后允许回绕,B 等于 int.MinValue。
关键点: 运行时算术默认 unchecked,常量表达式默认 checked。
0008 关于 const 与 static readonly 的跨程序集行为,哪项正确?
难度: 实战
- A. 修改
const后,引用程序集在运行时自动读到新值 - B.
const在编译期嵌入引用方,修改后必须重新编译所有引用程序集;static readonly无此问题 - C.
const在运行时通过元数据解析 - D. 只有
public的const会被嵌入
查看答案与解析
正确答案B
解析: const 的值在编译期被复制进引用方的 IL,更新定义方后不重编译引用方会继续使用旧值;static readonly 在类型初始化时读取,引用方无需重编译。
关键点: 公开 API 中对外契约的值用 static readonly 更安全。
0009 以下代码能否通过编译?
难度: 实战
struct Point
{
public int X;
}
class Holder
{
private readonly Point _p;
public void Move() => _p.X = 1;
}
- A. 不能,
readonly字段是结构体时,其成员不能在构造函数之外修改(CS1648) - B. 能
- C. 能编译,但运行抛异常
- D. 不能,结构体不允许公开字段
查看答案与解析
正确答案A
解析: 对 readonly 结构体字段修改成员会报 CS1648;这一点与引用类型字段不同——引用类型 readonly 只锁引用,结构体 readonly 连成员写入也禁止。
关键点: readonly 对值类型字段比对引用类型字段约束更强。
0010 以下代码中哪个方法能通过编译?
难度: 进阶
class Holder
{
private readonly StringBuilder _sb = new();
public void Clear() => _sb.Clear(); // 1
public void Swap() => _sb = new StringBuilder(); // 2
}
- A. 两个都能
- B. 两个都不能
- C. 第 1 个能(引用不可变,对象内容可变),第 2 个不能(CS0191)
- D. 第 1 个不能,第 2 个能
查看答案与解析
正确答案C
解析: readonly 只禁止字段引用被重新赋值;调用引用对象上的方法修改其内部状态是允许的,_sb = ... 则违反只读约束。
关键点: readonly ≠ 深度不可变。
0011 以下代码能否通过编译?
难度: 进阶
int x;
while (flag)
{
x = 1;
}
Console.WriteLine(x);
- A. 能
- B. 不能,循环体可能一次都不执行,
x未确定赋值(CS0165) - C. 能,但输出 0
- D. 能编译,运行抛异常
查看答案与解析
正确答案B
解析: while 循环体可能不执行,确定赋值分析不会假定循环至少进入一次,读取 x 报 CS0165。
关键点: 确定赋值对 while 与 do-while 的处理不同。
0012 以下代码输出什么?
难度: 进阶
int x;
do
{
x = 1;
} while (flag);
Console.WriteLine(x);
- A. 编译失败
- B. 输出 1,
do-while至少执行一次,满足确定赋值 - C. 输出 0
- D. 输出取决于
flag
查看答案与解析
正确答案B
解析: do-while 的循环体必然执行一次,确定赋值分析据此判定 x 已赋值,代码编译并输出 1。
关键点: 与 while 形成对比,这是高频陷阱。
0013 以下代码中哪些部分能通过编译?
难度: 实战
int a;
if (true) a = 1;
Console.WriteLine(a); // 1
int b;
if (false) b = 1;
Console.WriteLine(b); // 2
- A. 第 1 部分和第 2 部分都能编译
- B. 第 1 部分编译失败
- C. 第 2 部分编译失败,
if (false)分支不可达,b未确定赋值 - D. 两部分都编译失败
查看答案与解析
正确答案C
解析: 确定赋值分析对常量条件做折叠:if (true) 必然进入,a 已赋值;if (false) 永不进入,b 未确定赋值报 CS0165。
关键点: 编译器会利用常量条件判定可达性。
0014 以下代码能否通过编译?
难度: 进阶
int x = 1;
{
int x = 2;
Console.WriteLine(x);
}
- A. 不能,内层
x与外层局部变量作用域冲突(CS0136) - B. 能,输出 2
- C. 能,输出 1
- D. 不能,
x未确定赋值
查看答案与解析
正确答案A
解析: 局部变量不能在外层同名局部变量仍可见的范围内再次声明,即使在外层块中,也报 CS0136;这与字段可以被局部变量遮蔽不同。
关键点: 局部变量不允许遮蔽同方法内的外层局部变量。
0015 以下代码能否通过编译?
难度: 进阶
class Sample
{
private int _field;
public void Run()
{
int local;
_field = local;
}
}
- A. 能
- B. 不能,
local未确定赋值,即使只是把它赋给字段也会报 CS0165 - C. 不能,
_field是只读 - D. 不能,字段不能接收局部变量的值
查看答案与解析
正确答案B
解析: 读取未确定赋值的局部变量本身就是错误,与赋值目标无关;字段默认值只适用于字段,不改变局部变量的确定赋值规则。
关键点: 局部变量必须先赋值再读取,任何读取路径都受此约束。
0016 以下代码中哪些行无法编译?
难度: 进阶
var x = 1; // 1
var y; // 2
var z = null; // 3
- A. 第 1、2 行
- B. 第 2、3 行
- C. 全部
- D. 只有第 2 行
查看答案与解析
正确答案B
解析: var y; 没有初始化表达式,报 CS0818;var z = null; 无法推断类型,报 CS0815;var x = 1; 合法。
关键点: 隐式类型变量必须“有初始化且可推断”。
0017 以下 const 声明中哪一项无法编译?
难度: 实战
const string A = "a" + 1; // 1
const int B = 1 << 10; // 2
const double C = 1.0 / 3.0; // 3
const int D = sizeof(int); // 4
const object E = null; // 5
const int F = arr.Length; // 6
- A. 第 1 项
- B. 第 5 项
- C. 第 6 项,
arr.Length不是编译期常量表达式 - D. 全部都能编译
查看答案与解析
正确答案C
解析: 字符串常量拼接、常量位移、常量浮点除法、内置类型 sizeof、引用类型常量 null 都是合法常量表达式;数组的 Length 是运行时属性,报 CS0133。
关键点: 常量表达式的边界比多数人以为的更宽,但任何运行时求值都不行。
0018 以下代码输出什么?
难度: 进阶
var s = "x" + 1 + true;
Console.WriteLine(s);
- A.
x1True - B.
x1 - C. 编译失败
- D.
x1true
查看答案与解析
正确答案A
解析: 字符串加法从左到右结合:"x" + 1 得到 "x1",再 + true 调用 bool.ToString() 得到 "True"(大写 T),结果 "x1True"。
关键点: bool 的 ToString() 是大写 True/False;字符串拼接会隐式调用它。
0019 以下代码输出什么?
难度: 实战
class Config
{
public const int Base = 10;
private static int _a = Base + 1;
private static int _b = _a * 2;
public int Sum => _a + _b;
}
Console.WriteLine(new Config().Sum);
- A.
33 - B.
32 - C.
22 - D. 编译失败
查看答案与解析
正确答案A
解析: _a = 10 + 1 = 11,_b = 11 * 2 = 22,Sum = 33。const 参与静态字段初始化时按编译期值计算,不改变文本顺序语义。
关键点: 综合考察 const、静态初始化顺序与表达式求值。
0020 以下代码中哪些行能通过编译?
难度: 实战
byte b = 100;
b = b + 1; // 1
b += 1; // 2
- A. 两行都能编译
- B. 第 1 行编译失败(
b + 1是int,不能隐式转回byte),第 2 行编译成功 - C. 两行都不能编译
- D. 第 1 行能编译,第 2 行不能
查看答案与解析
正确答案B
解析: b + 1 提升为 int,显式赋回 byte 需要转换;复合赋值运算符自带一次隐式窄化转换,b += 1 等价于 b = (byte)(b + 1),合法。
关键点: 普通赋值与复合赋值对窄化的处理不同,是经典陷阱。