C# 试题 46:C# 14 新特性
0901 以下代码输出什么?
难度: 实战
class Counter
{
public int Value
{
get => field;
set => field = value;
}
}
Console.WriteLine(new Counter { Value = 3 }.Value);
- A. 编译失败,属性访问器中不能引用
field - B. 输出 3,
field引用编译器为该属性合成的后备字段 - C. 输出 0
- D. 运行抛
StackOverflowException
查看答案与解析
正确答案B
解析: C# 14 的 field 上下文关键字在属性访问器中指向编译器生成的合成后备字段:get => field 直接读它,set => field = value 写它,不再需要手写 _value 字段。赋值 Value = 3 落到后备字段,输出 3。
关键点: field 是访问器内引用合成后备字段的快捷方式。
0902 以下代码输出什么(注意类中已有名为 field 的成员)?
难度: 进阶
class C
{
int field = 5;
public int X { get => field; }
}
Console.WriteLine(new C().X);
- A. 输出 5,
field引用类中已有字段 - B. 编译失败,关键字与成员名冲突
- C. 输出 0 并产生警告 CS9258:
field仍绑定到合成的后备字段,想引用现有成员需写this.field或@field - D. 运行抛
NullReferenceException
查看答案与解析
正确答案C
解析: 在访问器中 field 优先按上下文关键字处理,绑定到合成的后备字段(初始值为 0),同时编译器给出 CS9258 警告提示冲突;类中那个 field = 5 的成员变成未使用。要引用现有成员必须显式写 this.field 或 @field。这是资深开发者最容易在存量代码上踩到的兼容性坑。
关键点: 已有 field 成员会让 field 的语义静默改变,需 this.field 消除歧义。
0903 以下代码能否编译?
难度: 实战
class C
{
public int X { get => field; set => field = value; }
public int Get() => field;
}
- A. 编译失败 CS0103:
field只存在于属性访问器中,普通方法里找不到该名称 - B. 编译通过,
Get()返回合成后备字段 - C. 编译通过,但运行抛异常
- D. 编译失败,
field与属性名X冲突
查看答案与解析
正确答案A
解析: field 的作用域限定在属性(或索引器)的访问器体内,普通方法、构造函数等位置没有这个名称,Get() 里引用 field 报 CS0103。想暴露后备字段只能走属性本身或手动声明真正的字段。
关键点: field 不是类级别成员,访问器之外不可见。
0904 关于 field 关键字的语义,哪项表述正确?
难度: 进阶
- A.
field可以在类的任意方法中访问所有属性的后备字段 - B. 属性已有显式后备字段时,
field自动指向该显式字段 - C.
field仅适用于class,不适用于struct与记录 - D.
field是仅限属性访问器使用的上下文关键字;与现有成员field冲突时关键字优先并产生警告,可用this.field/@field引用成员
查看答案与解析
正确答案D
解析: field 只在访问器内生效且绑定到合成后备字段;显式字段场景下不存在“自动指向”,仍以合成字段为准(冲突时靠警告提示)。struct 中同样可用。冲突处理用 this.field 或 @field 显式引用成员是最稳妥的写法。
关键点: field 的绑定规则是“合成字段优先 + 冲突警告”。
0905 以下代码输出什么?
难度: 实战
public static class StringExt
{
extension(string s)
{
public int WordCount => s.Split(' ').Length;
}
}
Console.WriteLine("a b c".WordCount);
- A. 编译失败,扩展块内只能声明扩展方法
- B. 输出 3,C# 14 扩展块可以声明扩展属性
- C. 输出 0
- D. 编译失败,扩展属性必须先有实例字段
查看答案与解析
正确答案B
解析: C# 14 的扩展成员块 extension (string s) { ... } 除了扩展方法,还能声明扩展属性、索引器等成员,块参数 s 即接收者。"a b c".WordCount 走扩展属性求值,Split(' ') 得到 3 段,输出 3。扩展成员按静态绑定调用,不修改 string 类型本身。
关键点: 扩展块成员以块参数为接收者,属性等成员同样可用。
0906 以下代码能否编译?
难度: 进阶
public static class Ext
{
extension(string s)
{
private int _count;
}
}
- A. 编译失败 CS9282:扩展块中不允许实例字段,扩展成员没有实例状态
- B. 编译通过,扩展块支持私有实例状态
- C. 编译通过,但运行抛异常
- D. 编译失败,
extension关键字不存在
查看答案与解析
正确答案A
解析: 扩展成员本质是静态绑定、作用在被扩展类型上的“视图”,编译器不会为被扩展类型注入任何实例字段,因此扩展块里声明字段直接报 CS9282。需要状态时只能通过接收者自身或外部字典等途径维护。
关键点: 扩展成员无实例状态,“给类型加字段”是错误直觉。
0907 以下代码能否编译?
难度: 进阶
class Box
{
extension(string s)
{
public int Len => s.Length;
}
}
- A. 编译通过,扩展只对
string生效 - B. 编译通过,但只能在该类内部调用
- C. 编译失败 CS9283:扩展必须在顶级非泛型静态类中声明
- D. 运行抛
TypeLoadException
查看答案与解析
正确答案C
解析: 扩展块和扩展方法一样,必须声明在“顶级非泛型静态类”中;写在普通实例类里报 CS9283。这是编译器对扩展成员归属的硬性要求,与被扩展类型是 string 无关。
关键点: 扩展块的宿主必须是顶级非泛型静态类。
0908 关于 C# 14 扩展成员块的运行机制,哪项正确?
难度: 进阶
- A. 扩展属性会在被扩展类型中注入实例字段
- B. 扩展成员按静态绑定规则调用,不会修改被扩展类型,也不增加其实例状态;仍可显式按静态成员形式调用
- C. 扩展方法的优先级高于被扩展类型自身的实例方法
- D. 扩展块必须声明在非静态类中才能访问实例状态
查看答案与解析
正确答案B
解析: 扩展成员只是一组以接收者为第一参数的静态成员,调用点在编译期静态解析:实例成员优先于扩展成员,扩展不会改变原类型布局,也不持有状态。“注入实例字段”“修改原类型”都是常见误解。
关键点: 扩展成员是静态绑定的语法糖,不改变被扩展类型。
0909 以下代码输出什么?
难度: 实战
class Order { public int Count { get; set; } }
static int SideEffect()
{
Console.WriteLine("RHS");
return 1;
}
Order? o = null;
o?.Count = SideEffect();
Console.WriteLine("done");
- A. 输出
RHS与done - B. 编译失败,空条件访问不能作为赋值目标
- C. 只输出
RHS - D. 只输出
done:接收者为 null 时整个赋值被跳过,右侧表达式不求值
查看答案与解析
正确答案D
解析: C# 14 允许 ?. 作为赋值目标(o?.Count = value)。当 o 为 null 时,整个赋值被跳过,SideEffect() 不会执行,因此只打印 done。若接收者非空,则正常求值并赋值。这一“短路到右侧不求值”的语义与 ?. 普通成员访问一致。
关键点: 空条件赋值在接收者为 null 时跳过,且不评估右侧。
0910 以下代码能否编译?
难度: 进阶
class Stats { public int Count { get; set; } }
Stats? s = null;
s?.Count++;
- A. 编译失败 CS1059:
++/--的操作数必须是变量、属性或索引器,空条件访问不满足 - B. 编译通过,等价于
s?.Count += 1 - C. 编译通过,但运行抛异常
- D. 编译通过,
s为 null 时Count变为 1
查看答案与解析
正确答案A
解析: 空条件赋值支持 =、复合赋值(+=、-= 等)以及事件的 +=/-=,但 ++/-- 不在支持范围:s?.Count++ 中 ?. 使操作数不再是“变量、属性或索引器”,报 CS1059。需要自增时写成 s?.Count += 1。
关键点: ++/-- 不适用空条件赋值,复合赋值可以。
0911 以下代码能否编译?
难度: 实战
class Notifier { public event Action? Changed; }
Notifier? n = null;
n?.Changed += () => Console.WriteLine("hi");
- A. 编译失败,事件不能与空条件赋值连用
- B. 编译通过,但运行抛
NullReferenceException - C. 编译通过:事件的
+=/-=得到专门支持,接收者为 null 时跳过订阅 - D. 编译通过,但事件必然被重复订阅
查看答案与解析
正确答案C
解析: 空条件赋值对事件 +=/-= 有专门支持:n?.Changed += handler 在 n 为 null 时跳过 add/remove 操作,非 null 时正常执行。这是 C# 14 为空条件赋值列出的显式场景之一,可用于安全订阅事件。
关键点: 事件 +=/-= 是空条件赋值的受支持形态。
0912 以下代码输出什么?
难度: 实战
class Stats { public int Count { get; set; } }
Stats? s = new Stats { Count = 5 };
s?.Count -= 2;
Console.WriteLine(s!.Count);
- A. 输出 5
- B. 输出 3:空条件复合赋值在接收者非空时正常执行
- C. 编译失败,复合赋值不能与
?.连用 - D. 运行抛
NullReferenceException
查看答案与解析
正确答案B
解析: s?.Count -= 2 是复合赋值:先判定 s 非 null,再执行 s.Count = s.Count - 2,得到 3。复合赋值是空条件赋值支持的形态之一,接收者非空时行为与普通赋值一致。s!.Count 只是告诉编译器此处可空引用已由逻辑保证非空。
关键点: 空条件复合赋值 = 判空后执行读-改-写。
0913 以下代码能否编译?
难度: 实战
Func<int, int> f = (ref int x) => x++;
- A. 编译通过,
Func<>支持带ref参数的 lambda - B. 编译失败,lambda 一律不允许
ref参数 - C. 编译通过,但运行抛异常
- D. 编译失败 CS1677:
Func<int,int>的参数没有ref修饰符,签名不匹配
查看答案与解析
正确答案D
解析: C# 14 允许 lambda 参数带 ref/out/in 修饰符,但 lambda 必须与目标委托的签名一致:Func<int,int> 的参数无 ref,而 lambda 声明了 ref,报 CS1677。要使用带 ref 的 lambda,必须选择或定义签名含 ref 的委托类型。
关键点: 修饰符参与委托签名匹配,Func<>/Action<> 不带 ref。
0914 以下代码输出什么?
难度: 实战
delegate void D(ref int x);
D d = (ref int x) => x++;
int n = 1;
d(ref n);
Console.WriteLine(n);
- A. 输出 1
- B. 输出 2,C# 14 允许 lambda 参数声明
ref/out/in - C. 编译失败,自定义委托也不接受
reflambda - D. 输出 0
查看答案与解析
正确答案B
解析: 委托 D 的签名带 ref int,lambda (ref int x) => x++ 与之匹配,d(ref n) 通过引用自增 n,输出 2。C# 14 之前这种写法是编译错误,现在只要委托签名一致即可。
关键点: 带修饰符的 lambda 需要签名匹配的自定义委托。
0915 以下代码输出什么?
难度: 进阶
var f = (ref int x) => x++;
int n = 1;
f(ref n);
Console.WriteLine(n);
- A. 输出 2:C# 14 下带
ref参数的 lambda 也能推断出自然类型并用var接收 - B. 编译失败,带
ref的 lambda 必须显式写出委托类型 - C. 输出 1
- D. 编译失败,
var不能接收 lambda
查看答案与解析
正确答案A
解析: C# 14 的 lambda 参数修饰符特性同时支持自然类型推断:(ref int x) => x++ 可推断为“ref int 参数、int 返回”的函数类型,var f 合法,f(ref n) 把 n 自增为 2。注意这与 0913 并不矛盾——Func<int,int> 签名不含 ref 所以不匹配,而自然类型推断完整保留了 ref 信息。
关键点: 自然类型推断保留修饰符信息,var 可以接住带 ref 的 lambda。
0916 以下代码输出什么?
难度: 进阶
delegate void DIn(in int x);
DIn d = (in int x) => Console.WriteLine(x);
int n = 5;
d(in n);
- A. 编译失败,
in参数不能用于 lambda - B. 输出 0
- C. 输出 5:
in修饰符同样受支持,且必须与委托签名一致 - D. 运行抛
InvalidOperationException
查看答案与解析
正确答案C
解析: in 与 ref/out 一样可以在 C# 14 的 lambda 参数中声明,前提是与目标委托签名匹配。DIn 带 in int,d(in n) 以只读引用传入,输出 5。in 防止被修改,但读取完全正常。
关键点: ref/out/in 三类修饰符都支持 lambda,签名必须一致。
0917 以下代码输出什么?
难度: 实战
Console.WriteLine(nameof(List<>));
Console.WriteLine(nameof(Dictionary<,>));
- A. 编译失败,
nameof不接受开放泛型类型 - B. 输出
List与Dictionary,nameof返回类型名且不含 arity 后缀 - C. 输出
List<>与Dictionary<,> - D. 输出
List1与Dictionary2
查看答案与解析
正确答案B
解析: C# 14 允许 nameof 作用于未绑定(开放)泛型类型:nameof(List<>) 返回 "List",nameof(Dictionary<,>) 返回 "Dictionary"。nameof 产生的是编译期字符串常量,不带反引号 arity 后缀,与 typeof(...).Name 不同。
关键点: nameof(开放泛型) 返回不含 arity 的类型名。
0918 以下代码输出什么?
难度: 进阶
Console.WriteLine(nameof(List<int>));
Console.WriteLine(typeof(List<>).Name);
- A.
List<int>与List - B.
List与List - C.
List1与List1 - D.
List与List1:nameof去掉类型实参,typeof的Name` 含反引号与 arity
查看答案与解析
正确答案D
解析: nameof(List<int>) 只取最外层类型名,得到 "List";typeof(List<>).Name 是运行时类型元数据名称,未绑定泛型用反引号加 arity 表示,即 "List1”`。两者语义不同,最容易记混。
关键点: nameof 是编译期字符串,Type.Name 是含 arity 的运行时名称。
0919 以下代码输出什么?
难度: 进阶
class Wrapper<T> { }
Console.WriteLine(nameof(Wrapper<>));
- A. 输出
Wrapper:nameof开放泛型适用于自定义泛型类型 - B. 编译失败,开放泛型只允许用于框架类型
- C. 输出
Wrapper<> - D. 运行抛
TypeLoadException
查看答案与解析
正确答案A
解析: nameof 开放泛型不限定类型来源,自定义 Wrapper<T> 同样适用:nameof(Wrapper<>) 返回 "Wrapper"。该特性解决的是“拿不到类型实参也要取类型名”的场景,与类型是否来自框架无关。
关键点: 开放泛型 nameof 对任意泛型类型有效。
0920 关于 nameof 开放泛型与 typeof 的差异,哪项正确?
难度: 进阶
- A.
typeof(List<>)在运行时抛异常 - B.
nameof(List<>)返回"List1”,与typeof(List<>).Name` 相同 - C.
nameof是编译期常量,nameof(List<>)与nameof(List<int>)都返回"List" - D.
nameof开放泛型只能配合#if指令使用
查看答案与解析
正确答案C
解析: nameof 的结果是编译期字符串常量,不关心类型实参是否补齐:开放与封闭写法都返回类型名 "List"。typeof(List<>) 返回未绑定 Type 对象(Name 为 "List1”`),不抛异常。二者用途不同:前者用于标识符/日志,后者用于反射。
关键点: nameof 与 typeof 对泛型 arity 的处理完全不同。