返回题库高级 .NET 刷题C# 选择题 · 第 9 / 50 篇

C# 试题 09:继承与多态

0161 以下代码输出什么?

难度: 实战

class A
{
    public virtual string M() => "A";
}

class B : A
{
    public override string M() => "B";
}

A a = new B();
Console.WriteLine(a.M());
  • A. A
  • B. AB
  • C. B,override 按实际类型动态分派
  • D. 编译失败
查看答案与解析

正确答案C

解析: override 改写虚槽,调用由对象实际类型决定;a 虽声明为 A,实际指向 B,分派到 B.M 输出 B

关键点: 虚调用看运行时类型,不看引用静态类型。

0162 以下代码输出什么?

难度: 实战

class A
{
    public string M() => "A";
}

class B : A
{
    public new string M() => "B";
}

A a = new B();
B b = new B();
Console.WriteLine(a.M() + b.M());
  • A. BB
  • B. AB,new 隐藏按编译期静态类型选择
  • C. AA
  • D. 编译失败
查看答案与解析

正确答案B

解析: new 只是隐藏基成员:通过 A 引用调用选中 A.M,通过 B 引用调用选中 B.M,输出 AB

关键点: new 是编译期静态绑定,与 override 的运行时分派完全不同。

0163 以下代码输出什么?

难度: 进阶

class A
{
    public virtual void M() => Console.Write("A");
}

class B : A
{
    public new virtual void M() => Console.Write("B");
}

class C : B
{
    public override void M() => Console.Write("C");
}

A a = new C();
B b = new C();
a.M();
b.M();
  • A. AB
  • B. BB
  • C. AC,B 用 new 开了新虚槽,A 的虚槽仍指向 A.M,C 覆盖的是 B 的虚槽
  • D. BC
查看答案与解析

正确答案C

解析: Bnew virtual 创建了一条独立虚链:A 的虚槽未被改写(输出 A),B 的新虚槽被 C 覆盖(输出 C)。

关键点: new virtual 会“分裂”虚槽,基类引用与派生类引用看到不同实现。

0164 以下代码能否编译?

难度: 进阶

class A
{
    public virtual void M() { }
}

class B : A
{
    public sealed override void M() { }
}

class C : B
{
    public override void M() { }
}
  • A. 编译失败,不能重写 sealed 成员(CS0239)
  • B. 编译通过
  • C. 编译通过,运行抛异常
  • D. 编译通过,但 C.M 不生效
查看答案与解析

正确答案A

解析: sealed override 在 B 处终结虚链,C 再写 override 编译报 CS0239。

关键点: sealed 只作用于“覆盖点”,它本身就是 override。

0165 以下代码能否编译?

难度: 实战

sealed class A { }

class B : A { }
  • A. 编译通过
  • B. 运行抛异常
  • C. 编译失败,不能从密封类型派生(CS0509)
  • D. 编译通过,但 B 不继承 A
查看答案与解析

正确答案C

解析: sealed 类禁止被继承,任何派生声明都是编译错误 CS0509。

关键点: sealed 类没有派生类,这是设计契约而非运行时行为。

0166 以下代码输出什么?

难度: 进阶

class A
{
    public A() => Console.Write(M() ?? "null");
    public virtual string? M() => "A";
}

class B : A
{
    private string? _s;
    public B() => _s = "B";
    public override string? M() => _s;
}

new B();
  • A. A
  • B. B
  • C. 编译失败
  • D. null,基类构造函数中调用虚方法会派发到 B.M(),但 B 的构造函数体尚未执行,_s 仍为 null
查看答案与解析

正确答案D

解析: 构造顺序是 B 的字段初始化 → A 的构造函数 → B 的构造函数体。A 的构造函数里调用虚方法 M() 会派发到 B.M,此时 B 的构造函数体尚未运行,_s 为 null,输出 null

关键点: 构造函数里调用虚方法会看到派生类“未初始化完成”的状态。

0167 以下代码输出什么?

难度: 实战

class A
{
    public virtual void M() => Console.Write("A");
}

class B : A
{
    public override void M()
    {
        base.M();
        Console.Write("B");
    }
}

new B().M();
  • A. AB
  • B. A
  • C. B
  • D. BA
查看答案与解析

正确答案A

解析: base.M() 显式调用基类实现,是非虚调用,输出 A 后再输出 B。

关键点: base 调用跳过虚分派,直接执行基类版本。

0168 以下代码输出什么?

难度: 进阶

class Animal { }
class Dog : Animal { }

class Shelter
{
    public virtual Animal Get() => new Animal();
}

class DogShelter : Shelter
{
    public override Dog Get() => new Dog();
}

Console.WriteLine(new DogShelter().Get().GetType().Name);
  • A. Animal
  • B. Shelter
  • C. 编译失败
  • D. Dog,协变返回允许用更派生的引用类型覆盖
查看答案与解析

正确答案D

解析: C# 9 起支持协变返回:覆盖方法可以返回基方法返回类型的派生类型,DogShelter.Get 返回 Dog

关键点: 协变返回只在“返回更派生的引用类型”时合法。

0169 以下代码能否编译?

难度: 进阶

class Animal { }

class Shelter
{
    public virtual Animal Get() => new Animal();
}

class CatShelter : Shelter
{
    public override string Get() => "x";
}
  • A. 编译失败,返回类型必须是 Animal 或其派生类型(CS0508)
  • B. 编译通过,任意类型都可覆盖
  • C. 编译通过,运行抛异常
  • D. 编译通过,返回 string
查看答案与解析

正确答案A

解析: stringAnimal 没有继承关系,协变返回不适用,覆盖签名必须一致,编译报 CS0508。

关键点: 协变返回要求返回类型是“更派生”的引用类型,无关类型不行。

0170 以下代码能否编译?

难度: 进阶

class A
{
    public virtual object M() => new();
}

class B : A
{
    public override int M() => 1;
}
  • A. 编译通过
  • B. 运行抛异常
  • C. 编译失败,协变返回仅适用于引用类型,int 不是 object 的引用派生类型(CS0508)
  • D. 编译通过,输出 1
查看答案与解析

正确答案C

解析: 虽然 int 能装箱成 object,但协变返回要求的是引用类型层面的继承关系,值类型不满足,报 CS0508。

关键点: 装箱转换不等于引用派生关系,协变返回只看类型层次。

0171 以下代码输出什么?

难度: 进阶

class A
{
    public virtual void M() => Console.Write("A");
}

class B : A
{
    public override void M() => Console.Write("B");
}

class C : B
{
    public new void M() => Console.Write("C");
}

A a = new C();
a.M();
  • A. A
  • B. C
  • C. ABC
  • D. B,C 用 new 隐藏的是 B 的虚实现,从 A 视角分派仍落到 B
查看答案与解析

正确答案D

解析: Cnew 只隐藏了“通过 C/B 静态类型可见”的 B.M,而 A 的虚槽仍指向 B.M(A 虚链上 B 是最后一个 override),经 A 引用调用输出 B

关键点: new 隐藏不影响基类虚槽的既有分派目标。

0172 以下代码输出什么?

难度: 实战

class A
{
    public int X => 1;
}

class B : A
{
    public new int X => 2;
}

A a = new B();
Console.WriteLine(a.X);
  • A. 2
  • B. 1
  • C. 编译失败
  • D. 运行抛异常
查看答案与解析

正确答案B

解析: 属性与字段一样可被 new 隐藏;通过 A 静态类型访问选中 A.X,输出 1。

关键点: new 隐藏同样适用于属性,静态类型决定取哪个成员。

0173 以下代码能否编译?

难度: 实战

abstract class A
{
    public abstract void M();
}

class B : A { }
  • A. 编译通过
  • B. 运行抛异常
  • C. 编译失败,B 未实现抽象成员 M(CS0534)
  • D. 编译通过,但 B 不能实例化
查看答案与解析

正确答案C

解析: 非抽象派生类必须实现所有继承的抽象成员;B 未实现 M,编译报 CS0534。

关键点: 抽象成员是强制契约,首次出现的非抽象派生类必须兑现。

0174 以下代码输出什么?

难度: 实战

abstract class A
{
    public virtual void M() => Console.Write("A");
}

class B : A { }

new B().M();
  • A. 编译失败,抽象类的成员必须 abstract
  • B. 运行抛异常
  • C. 输出空
  • D. A,抽象类可以提供 virtual 默认实现,派生类可继承
查看答案与解析

正确答案D

解析: 抽象类可以有非抽象成员;virtual 默认实现允许派生类直接继承,B 无需重写即可调用,输出 A

关键点: abstract 修饰的是类型或成员本身,不排斥 virtual 实现。

0175 以下代码输出什么?

难度: 实战

class A
{
    public virtual void M() => Console.Write("A");
}

class B : A
{
    public override void M() => Console.Write("B");
}

class C : B
{
    public override void M()
    {
        base.M();
        Console.Write("C");
    }
}

new C().M();
  • A. ABC
  • B. BC
  • C. AC
  • D. C
查看答案与解析

正确答案B

解析: C.M 里的 base.M() 是静态调用,落到 B.M 输出 B,随后输出 C。

关键点: 多层 override 中 base 只上跳一层,不是一路跳到最基类。

0176 以下代码能否编译?

难度: 实战

class A
{
    public void M() { }
}

class B : A
{
    public override void M() { }
}
  • A. 编译失败,基成员未标记 virtual/abstract/override(CS0506)
  • B. 编译通过
  • C. 编译通过,运行抛异常
  • D. 编译通过,但隐藏基成员
查看答案与解析

正确答案A

解析: override 要求基类存在可覆盖的 virtual/abstract/override 成员;A.M 是普通方法,编译报 CS0506。

关键点: 想覆盖先声明 virtual,否则只能 new 隐藏。

0177 以下代码输出什么?

难度: 进阶

class A
{
    public virtual void M() => Console.Write("A");
}

class B : A
{
    public override void M()
    {
        base.M();
        Console.Write("B");
    }
}

class C : B
{
    public override void M()
    {
        base.M();
        Console.Write("C");
    }
}

new C().M();
  • A. BC
  • B. AC
  • C. ABC
  • D. AB
查看答案与解析

正确答案C

解析: C.Mbase.M()(即 B.M),B.M 又调 base.M()(即 A.M),逐层输出 A、B、C。

关键点: 每层 override 里的 base 都指向其直接基类实现,形成链式执行。

0178 关于 overridenew 的分派差异,哪项正确?

难度: 实战

  • A. override 参与动态分派(由实际类型决定);new 只在编译期按静态类型隐藏成员,通过基类引用调用不会到达 new 成员
  • B. overridenew 都是运行时动态分派
  • C. new 只能在非 virtual 成员上使用
  • D. override 要求基成员必须是 new
查看答案与解析

正确答案A

解析: 二者的本质区别是分派时机:override 改写虚槽、运行时生效;new 建立同名新成员、编译期按静态类型选择。new 同样可用于隐藏 virtual 成员,且 override 与 new 互不要求。

关键点: 判断输出先看“静态类型 vs 实际类型”,再看成员是 override 还是 new。

0179 关于 sealed override,哪项正确?

难度: 实战

  • A. sealed override 会取消 override 语义,变成静态调用
  • B. sealed override 终止虚链,防止更下层派生类继续覆盖,但它本身仍是 override
  • C. sealed 只能用于类,不能用于方法
  • D. sealed overridenew 等价
查看答案与解析

正确答案B

解析: sealed override 既改写虚槽(仍是多态分派目标),又禁止再被下层覆盖;sealed 可以修饰类、方法、属性等,与 new 无关。

关键点: sealed override = “仍是 override,但这是虚链终点”。

0180 关于协变返回,哪项正确?

难度: 进阶

  • A. 协变返回允许改变参数列表
  • B. 协变返回允许任意改变返回类型
  • C. 协变返回只对值类型有效
  • D. 覆盖方法可在语言允许时返回比基方法返回类型更派生的引用类型,参数必须与基签名一致;C# 9 起支持
查看答案与解析

正确答案D

解析: 协变返回只放宽“返回类型”这一维,且必须是更派生的引用类型;参数类型必须与基签名完全一致(C# 不支持参数逆变覆盖)。

关键点: 协变是返回方向的单向放宽,签名其余部分不变。

官方资料

当前分类

C# 选择题

查看全部分类 →
  1. 07C# 试题 07:类与构造函数20 题
  2. 08C# 试题 08:访问级别与程序集20 题
  3. 09C# 试题 09:继承与多态20 题
  4. 10C# 试题 10:接口与抽象类20 题
  5. 11C# 试题 11:结构体与 ref struct20 题
ESC

输入关键词开始搜索