C# 试题 07:类与构造函数
0121 以下代码输出什么?
难度: 实战
class A
{
public int X = Get();
public A() => Console.WriteLine($"ctor:{X}");
static int Get()
{
Console.Write("init ");
return 5;
}
}
new A();
- A.
init ctor:5 - B.
ctor:0 - C.
ctor:5 init - D. 编译失败
查看答案与解析
正确答案A
解析: 实例字段初始化器在构造函数体之前按声明顺序执行:先 init ,再进入构造函数体输出 ctor:5。
关键点: 字段初始化先于构造函数体,但晚于分配对象。
0122 以下代码输出什么?
难度: 进阶
class Base
{
public Base() => Console.Write("B");
}
class Derived : Base
{
private int _x = Init();
public Derived() => Console.Write("D");
static int Init()
{
Console.Write("I");
return 1;
}
}
new Derived();
- A.
BID - B.
BDI - C.
IBD - D.
DIB
查看答案与解析
正确答案C
解析: 构造顺序为:派生类字段初始化器(I)→ 基类构造函数体(B)→ 派生类构造函数体(D)。
关键点: 派生字段初始化先于基类构造函数执行,这是最常见的构造顺序误区。
0123 以下代码输出什么?
难度: 进阶
class C
{
public static int N = 5;
static C() => Console.WriteLine("static ctor");
public C() => Console.WriteLine("instance ctor");
}
Console.WriteLine(C.N);
new C();
new C();
- A.
instance ctor先输出,static ctor在第一次创建实例后才执行 - B. 输出
static ctor、5、instance ctor、instance ctor - C. 输出
5、static ctor、instance ctor、instance ctor - D.
static ctor每次创建实例都会执行
查看答案与解析
正确答案B
解析: 显式静态构造函数在首次访问该类型(读取静态字段 N)之前运行且只运行一次,随后打印 5,两次 new C() 各打印一次 instance ctor。
关键点: 静态构造函数是“首次使用类型前、仅一次”,且先于静态字段的首次读取。
0124 以下代码输出什么?
难度: 进阶
class C
{
public static int A = GetA();
static C() => Console.WriteLine($"static ctor A={A}");
static int GetA()
{
Console.Write("GetA ");
return 1;
}
}
Console.WriteLine(C.A);
- A. 输出
GetA 1(无 static ctor 输出) - B. 输出
static ctor A=0 GetA 1 - C. 编译失败
- D. 输出
GetA static ctor A=1,随后输出1
查看答案与解析
正确答案D
解析: 静态字段初始化器(GetA )先于静态构造函数体执行,然后运行 static ctor 打印 A=1,最后主程序读取 A 打印 1。
关键点: 静态字段初始化按声明顺序先跑完,静态构造函数体最后执行。
0125 以下代码输出什么?
难度: 实战
class Point(int x, int y)
{
public int Sum => x + y;
}
Console.WriteLine(new Point(3, 4).Sum);
- A.
0 - B. 编译失败
- C.
7 - D.
12
查看答案与解析
正确答案C
解析: 主构造函数参数 x、y 在整个类型体内可见,属性 Sum 计算 3 + 4 = 7。
关键点: 主构造函数参数是类型的词法作用域成员,无需字段即可在成员中引用。
0126 以下代码输出什么?
难度: 实战
class Box(int w)
{
private int _w = w;
public int W => _w;
}
Console.WriteLine(new Box(9).W);
- A.
0 - B. 编译失败,字段初始化器不能访问主构造函数参数
- C.
9 - D. 运行抛异常
查看答案与解析
正确答案C
解析: 主构造函数参数可以在字段初始化器中使用:_w 在构造时被赋为 9。
关键点: 普通 class 的主构造函数参数不会自动成为属性,需要显式存储到字段/属性。
0127 以下代码输出什么?
难度: 实战
class C(int x)
{
public C() : this(1) { }
public int X => x;
}
Console.WriteLine(new C().X);
- A.
1 - B.
0 - C. 编译失败
- D. 运行抛异常
查看答案与解析
正确答案A
解析: 带主构造函数的类型仍可声明额外构造函数,额外构造函数必须通过 this(...) 链接到主构造函数;new C() 经 this(1) 传入参数,X 为 1。
关键点: 额外构造函数要用 this(...) 显式把参数交给主构造函数,否则编译失败。
0128 以下代码输出什么?
难度: 实战
class C
{
public int X { get; set; }
public C() => Console.Write($"ctor:{X} ");
}
var c = new C { X = 7 };
Console.WriteLine($"after:{c.X}");
- A.
ctor:7 after:7 - B.
ctor:0 after:0 - C.
ctor:0 after:7,对象初始化器在构造函数之后执行 - D. 编译失败
查看答案与解析
正确答案C
解析: 对象初始化器在构造函数返回后执行:构造时 X 仍为默认值 0,初始化器随后把 X 设为 7。
关键点: 构造函数内读不到对象初始化器设置的值,初始化器执行时机在构造之后。
0129 以下代码输出什么?
难度: 进阶
class Inner
{
public int V { get; set; }
}
class Outer
{
public Inner I { get; } = new Inner();
}
var o = new Outer { I = { V = 5 } };
Console.WriteLine(o.I.V);
- A.
0 - B. 编译失败,
I是只读属性 - C. 运行抛
NullReferenceException - D.
5,嵌套初始化器对现有对象赋值而不是重新创建
查看答案与解析
正确答案D
解析: I = { V = 5 } 是嵌套对象初始化器,等价于 o.I.V = 5,不会给只读属性 I 重新赋值,因此合法且输出 5。
关键点: 嵌套初始化器改的是成员对象的属性,不是替换外层属性引用。
0130 以下代码输出什么?
难度: 实战
class C
{
public C() : this(42)
{
Console.Write("default ");
}
public C(int x) => Console.Write($"int:{x} ");
}
new C();
- A.
int:42 default - B.
default int:42 - C.
int:42 - D. 编译失败
查看答案与解析
正确答案A
解析: this(...) 链会先执行被委托的构造函数 C(int),返回后再执行当前构造函数体,输出 int:42 default 。
关键点: this 构造链的执行顺序是“被委托者先、委托者后”。
0131 以下代码能否编译?
难度: 进阶
class Base
{
public Base() { }
}
class Derived : Base
{
public Derived() : this(1), base() { }
public Derived(int x) { }
}
- A. 能编译
- B. 编译失败,构造函数初始化器不能同时使用
this和base - C. 能编译,运行抛异常
- D. 能编译,但会创建两个对象
查看答案与解析
正确答案B
解析: 构造函数初始化器只能二选一:要么 this(...) 链到同类构造函数,要么 base(...) 链到基类;同时使用是编译错误。
关键点: this 与 base 是互斥的构造链入口,基类链由最终那个非 this 构造函数负责。
0132 以下代码输出什么?
难度: 进阶
class C
{
static C() => throw new InvalidOperationException("boom");
}
try
{
var c = new C();
}
catch (TypeInitializationException e)
{
Console.WriteLine(e.InnerException?.GetType().Name);
}
- A. 输出
TypeInitializationException - B. 输出
InvalidOperationException,静态构造函数异常被包装为TypeInitializationException - C. 编译失败
- D. 输出空
查看答案与解析
正确答案B
解析: 静态构造函数抛出的任何异常都会以 TypeInitializationException 包装后传播,InnerException 是原始异常,因此打印 InvalidOperationException。
关键点: 捕获静态初始化失败时要拆 InnerException 才能看到根因。
0133 以下代码输出什么?
难度: 实战
class Base
{
public Base(int x) => Console.Write(x);
}
class Derived : Base
{
public Derived() : base(5) { }
}
new Derived();
- A.
0 - B. 编译失败
- C.
5 - D. 运行抛异常
查看答案与解析
正确答案C
解析: base(5) 显式调用带参数的基类构造函数,输出 5。
关键点: 基类没有无参构造函数时,派生类必须用 base(...) 显式链接。
0134 以下代码能否编译?
难度: 实战
class Base
{
public Base(int x) { }
}
class Derived : Base { }
- A. 编译失败,没有可用的无参
base()构造(CS7036) - B. 能编译,使用隐式基类构造函数
- C. 能编译,运行抛异常
- D. 能编译,
x默认为0
查看答案与解析
正确答案A
解析: 派生类的隐式构造函数会调用 base(),但基类只有 Base(int),没有可匹配的无参构造函数,编译报 CS7036。
关键点: “基类必须有可调用的构造函数”是继承的基本约束。
0135 以下代码输出什么?
难度: 实战
class Base(int v)
{
public int V => v;
}
class Derived(int v, int extra) : Base(v)
{
public int Extra => extra;
}
Console.WriteLine(new Derived(1, 2).V + new Derived(1, 2).Extra);
- A.
1 - B.
2 - C. 编译失败
- D.
3
查看答案与解析
正确答案D
解析: 派生主构造函数用 Base(v) 把 v 传给基类主构造函数,V 为 1、Extra 为 2,和为 3。
关键点: 主构造函数之间的继承链接就是把参数显式传给 base(...)。
0136 以下代码输出什么?
难度: 进阶
class Bag
{
private Dictionary<string, int> _d = new();
public int this[string k]
{
get => _d.TryGetValue(k, out var v) ? v : 0;
set => _d[k] = value;
}
}
var b = new Bag { ["a"] = 1, ["b"] = 2 };
Console.WriteLine(b["a"] + b["b"]);
- A.
3 - B.
0 - C. 编译失败,对象初始化器不支持索引器
- D. 运行抛异常
查看答案与解析
正确答案A
解析: 对象初始化器支持索引器初始化:["a"] = 1、["b"] = 2 分别调用 setter,随后求和得 3。
关键点: 对象初始化器不仅能设置属性,也能设置索引器元素。
0137 以下代码输出什么?
难度: 进阶
class C
{
public static int X = 1;
static C() => X = 2;
public static int Y = 3;
}
Console.WriteLine(C.X + C.Y);
- A.
4 - B.
6 - C.
5 - D.
3
查看答案与解析
正确答案C
解析: 静态字段初始化器按声明顺序执行:X = 1、Y = 3,随后才运行静态构造函数体把 X 改为 2,所以 2 + 3 = 5。
关键点: 静态构造函数体在“全部静态字段初始化之后”运行,改写字段会影响最终值。
0138 关于静态构造函数,哪项正确?
难度: 实战
- A. 静态构造函数在首次访问类型(创建实例或访问静态成员)前运行,且只执行一次;不能显式调用,不能带参数
- B. 静态构造函数每次创建实例都会执行
- C. 静态构造函数可以带参数并显式调用
- D. 静态构造函数中抛出异常不会传播
查看答案与解析
正确答案A
解析: 静态构造函数是 CLR 触发的类型初始化入口:无参数、不可显式调用、进程内每个 AppDomain 至多一次;内部异常会包装为 TypeInitializationException 传播。
关键点: 静态构造的触发时机是“首次使用”,与实例构造次数无关。
0139 关于主构造函数与 record,哪项正确?
难度: 进阶
- A. class 的主构造函数参数会自动生成公开属性
- B. 主构造函数只能有一个参数
- C. 主构造函数与对象初始化器等价
- D. 主构造函数参数是类的词法作用域一部分;普通 class 不会自动存储为成员,record 的位置参数才会自动生成属性
查看答案与解析
正确答案D
解析: 主构造函数的参数可在字段初始化器、属性与方法中直接引用,但普通 class 需要显式赋值给字段/属性保存;record 的位置参数(record Point(int X, int Y))会自动生成公开属性。
关键点: “有主构造函数参数”不等于“自动成为成员”,class 与 record 行为不同。
0140 关于对象初始化器,哪项正确?
难度: 实战
- A. 对象初始化器可以给只读属性赋值
- B. 对象初始化器在构造函数之后执行,可对可写属性/字段或索引器赋值;只读属性只能由构造函数设置
- C. 对象初始化器在构造函数之前执行
- D. 对象初始化器只能用于无构造函数类型
查看答案与解析
正确答案B
解析: 初始化器本质是构造函数返回后的一组赋值语句,因此只读属性(无 setter)无法在其中赋值;有构造函数的类型照样可以用初始化器。
关键点: 初始化器的时机(构造之后)决定了它能写什么、不能写什么。