C# 试题 06:方法与参数
0101 以下代码能否编译?
难度: 实战
static void Add(ref int x) => x += 10;
int y;
Add(ref y);
Console.WriteLine(y);
- A. 能编译,输出
10 - B. 能编译,输出
0 - C. 能编译,运行抛异常
- D. 编译失败,
y在传引用前必须已赋值(CS0165)
查看答案与解析
正确答案D
解析: ref 要求实参在调用前处于“已明确赋值”状态;int y; 只是声明未赋值,Add(ref y) 编译报 CS0165。
关键点: ref 是“可读可写”引用,调用方必须先赋值。
0102 以下代码输出什么?
难度: 实战
int[] arr = { 1, 2, 3 };
ref int r = ref arr[1];
r = 99;
Console.WriteLine(arr[1]);
- A.
2 - B.
99 - C.
3 - D. 编译失败,数组元素不能取引用
查看答案与解析
正确答案B
解析: ref int r = ref arr[1] 让 r 成为数组元素的别名,给 r 赋值即修改 arr[1]。
关键点: ref 局部变量是存储位置的别名,不是值拷贝。
0103 以下代码输出什么?
难度: 实战
static bool TryGet(out int value)
{
value = 42;
return true;
}
TryGet(out int result);
Console.WriteLine(result);
- A. 编译失败,
result未初始化 - B. 能编译,输出
0 - C. 能编译,运行抛异常
- D. 能编译,输出
42
查看答案与解析
正确答案D
解析: out 不要求调用前赋值,方法内为 value 赋值 42,result 拿到该值。
关键点: out 的“必须赋值”义务在方法内部,调用方只需提供变量。
0104 以下代码输出什么?
难度: 实战
static void Get(out string s) => s = "hi";
Get(out var msg);
Console.WriteLine(msg);
- A. 能编译,输出
hi - B. 编译失败,
out var不合法 - C. 能编译,输出空字符串
- D. 能编译,输出
null
查看答案与解析
正确答案A
解析: out var msg 是 out 变量声明,编译器在方法调用处内联声明变量并接收赋值。
关键点: out var 把声明与传参合二为一,变量在调用点立即可用。
0105 以下代码能否编译?
难度: 进阶
static void M(in int x)
{
x++;
}
- A. 编译失败,
in参数是只读的,不能赋值(CS8331) - B. 能编译,
x加 1 但不影响调用方 - C. 能编译,运行抛异常
- D. 能编译,
x加 1 且影响调用方
查看答案与解析
正确答案A
解析: in 是只读引用参数,方法内对其赋值(含自增)属于编译错误 CS8331。
关键点: in 提供“引用传递的只读视图”,禁止写入。
0106 以下代码能否编译并运行?
难度: 进阶
static void M(in int x) => Console.WriteLine(x);
M(42);
- A. 编译失败,字面量不能传给
in参数 - B. 能编译,输出
0 - C. 能编译,运行抛异常
- D. 能编译,输出
42,编译器为字面量创建临时变量
查看答案与解析
正确答案D
解析: 实参不是变量时,编译器会创建一个临时变量以只读引用方式传入,因此 M(42) 合法且输出 42。
关键点: in 并不保证“一定按引用传原存储位置”,无法安全取址时会有临时副本。
0107 以下代码输出什么?
难度: 实战
static void M(int a = 1, int b = 2, int c = 3)
{
Console.WriteLine($"{a}{b}{c}");
}
M(c: 9);
- A.
139 - B.
123 - C.
129 - D. 编译失败
查看答案与解析
正确答案C
解析: 命名实参 c: 9 只覆盖第三个参数,a、b 用默认值,输出 129。
关键点: 命名实参可以跳过中间参数,未提供的参数用默认值。
0108 以下代码能否编译?
难度: 进阶
static void M(int x = DateTime.Now.Year)
{
}
- A. 编译失败,默认参数值必须是编译时常量(CS1736)
- B. 能编译,默认值在运行时计算
- C. 能编译,运行抛异常
- D. 能编译,默认值为
0
查看答案与解析
正确答案A
解析: 可选参数的默认值写入元数据,必须是编译期常量;DateTime.Now.Year 是运行时值,编译报 CS1736。
关键点: 可选参数默认值是“常量契约”,不是运行时求值表达式。
0109 以下代码输出什么?
难度: 实战
static int Sum(params int[] xs)
{
int t = 0;
foreach (var v in xs) t += v;
return t;
}
Console.WriteLine(Sum());
Console.WriteLine(Sum(1, 2, 3));
- A. 编译失败
- B.
0和0 - C.
0和6 - D. 运行抛异常
查看答案与解析
正确答案C
解析: params 允许零个或多个实参:Sum() 得到长度为 0 的空数组,Sum(1, 2, 3) 求和为 6。
关键点: params 调用不传实参是合法的,不会抛异常。
0110 以下代码运行结果是什么?
难度: 进阶
static void M(params object[] args)
{
Console.WriteLine(args.Length);
}
M(null);
- A. 输出
0 - B. 输出
1 - C. 运行抛
NullReferenceException,null被当作整个数组而不是单个元素 - D. 编译失败
查看答案与解析
正确答案C
解析: 当实参能直接匹配数组类型时,编译器选择“数组形式”而不是“展开形式”,args 本身是 null,访问 args.Length 抛 NullReferenceException。
关键点: M(null) 传的是整个 params 数组,不是数组里的一个 null 元素。
0111 以下代码输出什么?
难度: 实战
static void M(string a, string b = "B", string c = "C")
{
Console.WriteLine(a + b + c);
}
M("A", c: "X");
- A.
ABC - B.
AXC - C. 编译失败
- D.
ABX
查看答案与解析
正确答案D
解析: 位置实参 "A" 给 a,命名实参 c: "X" 给 c,中间的 b 用默认值 "B",拼接为 ABX。
关键点: C# 7.2 起位置实参后可以跟命名实参,只要顺序不乱。
0112 以下代码输出什么?
难度: 进阶
static ref int Get(int[] arr, int i) => ref arr[i];
int[] nums = { 10, 20, 30 };
Get(nums, 1) = 99;
Console.WriteLine(nums[1]);
- A. 编译失败,ref 返回不能赋值
- B.
20 - C.
30 - D.
99
查看答案与解析
正确答案D
解析: ref 返回方法返回的是数组元素的引用,可以直接作为赋值目标,nums[1] 被改为 99。
关键点: ref 返回把“方法结果”变成“可写的存储位置”。
0113 以下代码能否编译?
难度: 进阶
static void M(ref int x)
{
Action a = () => x++;
}
- A. 编译失败,
ref/out/in参数不能用于 lambda 或本地函数(CS1628) - B. 能编译,闭包捕获引用
- C. 能编译,运行抛异常
- D. 能编译,但 lambda 不生效
查看答案与解析
正确答案A
解析: 闭包捕获需要稳定的存储位置,ref 参数指向调用方的任意存储,无法安全捕获,编译报 CS1628。
关键点: 想要闭包修改外部值,应使用普通局部变量并在调用时传 ref。
0114 以下代码输出什么?
难度: 实战
static void M(params int[] xs) => Console.WriteLine(xs.Length);
int[] arr = { 1, 2, 3, 4 };
M(arr);
- A.
1 - B. 编译失败
- C.
4 - D. 运行抛异常
查看答案与解析
正确答案C
解析: 实参本身是 int[] 时采用数组形式,直接传递原数组,不再创建新数组,长度 4。
关键点: 传入数组对象时 params 不会复制,热路径可借此避免分配。
0115 以下代码输出什么?
难度: 进阶
static void M(int x, int y = 2) => Console.WriteLine("A");
static void M(int x) => Console.WriteLine("B");
M(1);
- A. 输出
B,无需补全可选参数的重载更优 - B. 输出
A - C. 编译失败,重载存在歧义
- D. 运行抛异常
查看答案与解析
正确答案A
解析: 两个重载都能以 1 个实参调用,但 M(int x) 未省略任何可选参数,按“省略可选参数更少者优先”的规则胜出,输出 B。
关键点: 带可选参数的重载会让“看似重复”的签名并存,选优规则容易被忽略。
0116 以下代码输出什么?
难度: 进阶
static void M(in int x) => Console.WriteLine("in");
static void M(int x) => Console.WriteLine("value");
int n = 5;
M(n);
- A. 输出
value,普通按值参数优先于in参数 - B. 输出
in - C. 编译失败,重载存在歧义
- D. 运行抛异常
查看答案与解析
正确答案A
解析: 对普通实参,按值参数在重载解析中优于按 in 引用传递的参数;只有显式写 M(in n) 才会选 in 重载。
关键点: 新增 in 重载不会劫持既有按值调用,但会让调用点行为取决于写法。
0117 以下代码能否编译?若编译,运行结果是什么?
难度: 进阶
static int M(out int x)
{
throw new Exception("boom");
}
int v;
M(out v);
- A. 编译失败,
out参数必须在方法返回前赋值 - B. 能编译,输出
0 - C. 能编译,运行抛
UnassignedVariableException - D. 能编译,方法直接抛异常无需给
out赋值,运行抛Exception
查看答案与解析
正确答案D
解析: 确定性赋值规则只要求“正常返回路径上 out 已赋值”;方法直接抛异常时不需要赋值,编译通过,运行时抛 Exception。
关键点: out 的赋值义务仅覆盖正常返回路径。
0118 关于 ref 与 out 的契约,哪项正确?
难度: 实战
- A.
ref和out完全等价 - B.
ref要求调用前变量已赋值,方法可读写该存储位置;out不要求调用前赋值,但方法必须赋值后才能正常返回 - C.
out调用前必须赋值,ref不必 - D. 两者都不能用于方法签名重载
查看答案与解析
正确答案B
解析: ref 是读写引用,调用方负有初始化义务;out 把初始化义务转移给方法。二者仅修饰符不同即可构成重载,但调用点语法不同。
关键点: ref 义务在调用方、out 义务在方法内。
0119 关于 in 参数的真实语义,哪项正确?
难度: 进阶
- A.
in是只读引用传递;当实参不是变量或无法安全取地址时,编译器会复制到临时变量再传入 - B.
in参数保证总是按引用传递,从不复制 - C.
in与ref完全相同 - D.
in参数可以在方法内重新赋值
查看答案与解析
正确答案A
解析: in 优先以只读引用传递,但实参为字面量、常量、表达式或属性返回值时会产生临时副本,因此“零复制”不是保证,大结构体也未必省下拷贝。
关键点: in 是“可复制也可引用”的只读语义,性能收益要看实参形态。
0120 关于 params 集合(C# 13 起),哪项正确?
难度: 进阶
- A.
params只能是数组 - B.
params调用必须至少传一个实参 - C.
params允许零个或多个实参,C# 13 起支持Span<T>/ReadOnlySpan<T>、List<T>等集合类型;热路径需关注是否产生临时分配 - D.
params集合不能在调用时省略参数
查看答案与解析
正确答案C
解析: C# 13 把 params 从“仅数组”扩展到支持 Span、ReadOnlySpan 及实现集合模式的类型;调用方依旧可传零个或多个实参。Span 形式可避免堆分配,而 List<T> 等引用类型集合在展开调用时仍会分配。
关键点: params 集合解决的是“参数个数不定”,分配与否取决于集合类型与调用形式。