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

C# 试题 10:接口与抽象类

0181 以下代码输出什么?

难度: 实战

interface IA { void M(); }
interface IB { void M(); }

class C : IA, IB
{
    public void M() => Console.WriteLine("C");
}

var c = new C();
c.M();
((IA)c).M();
((IB)c).M();
  • A. 编译失败,一个类无法用一个成员同时满足两个接口
  • B. 三行都输出 C,单个公共方法可同时满足两个同名接口成员
  • C. 第一行编译失败,c.M() 有歧义
  • D. 运行抛 AmbiguousMatchException
查看答案与解析

正确答案B

解析: 两个接口声明了同名成员时,一个隐式公共实现可以同时满足二者;类引用与两个接口引用调用都分发到 C.M。显式接口实现只是“需要区分时”的手段,不是必须。

关键点: 多个接口的同名成员不要求显式实现,一个公共方法即可。

0182 关于「接口多继承」与类继承的关系,哪项正确?

难度: 进阶

  • A. 一个类可以通过“实现多个接口”获得多个基类的对象布局
  • B. 类可实现多个接口,但只能直接继承一个类;接口之间也可以互相继承(接口多继承),无默认实现时实现类需提供全部接口成员
  • C. 接口不能继承其他接口
  • D. 实现多个接口时,每个同名成员都必须显式实现
查看答案与解析

正确答案B

解析: 「接口多继承」指的是接口可以继承多个接口、类可以实现多个接口;而类的继承仍然是单继承。接口不携带实例状态,所以“多实现”不会引入多份对象布局。

关键点: 多实现 ≠ 多继承;接口多继承只在接口层面成立。

0183 以下代码能否编译?

难度: 实战

interface IWidget
{
    void Paint() => Console.WriteLine("Default");
}

class Widget : IWidget { }

var w = new Widget();
w.Paint();
  • A. 编译失败,Widget 没有名为 Paint 的成员,默认接口实现只能通过接口引用调用
  • B. 输出 Default
  • C. 编译失败,接口成员不能有方法体
  • D. 运行抛 MissingMethodException
查看答案与解析

正确答案A

解析: 默认接口实现不会变成实现类的公共成员;Widget 未声明 Paintw.Paint() 无法编译。改成 ((IWidget)w).Paint() 才会调用默认实现输出 Default

关键点: 默认接口实现“继承”的是接口引用,不是类成员表。

0184 以下代码输出什么?

难度: 实战

interface IWidget
{
    void Paint() => Console.WriteLine("Default");
}

class Widget : IWidget
{
    public void Paint() => Console.WriteLine("Override");
}

Widget w = new Widget();
IWidget i = w;
w.Paint();
i.Paint();
  • A. 两行都输出 Default
  • B. 第一行输出 Default,第二行输出 Override
  • C. 编译失败,类成员与默认实现冲突
  • D. 两行都输出 Override
查看答案与解析

正确答案D

解析: 默认实现只是“没有实现时的兜底”。类一旦提供同名公共实现,无论通过类引用还是接口引用调用,都使用类实现。

关键点: 类实现永远优先于默认接口实现。

0185 以下代码能否编译?

难度: 进阶

interface IA
{
    void M() => Console.WriteLine("A");
}

interface IB : IA
{
    void M();
}

class C : IB { }
  • A. 编译通过,C 继承 IA 的默认实现
  • B. 编译失败,IB 重新抽象了 MC 必须提供实现
  • C. 编译失败,接口成员不能既带默认实现又被重新抽象
  • D. 编译通过,但运行抛 NotImplementedException
查看答案与解析

正确答案B

解析: 派生接口可以用不带方法体的声明“重新抽象”(reabstraction)基接口的默认实现;实现该派生接口的类必须实现该成员,不能继续依赖被撤销的默认实现。

关键点: 重新抽象会撤销默认实现的“兜底”。

0186 以下代码能否编译?

难度: 实战

abstract class Shape
{
    public abstract double Area();
}

class Circle : Shape { }
  • A. 编译通过,Circle 自动继承抽象方法
  • B. 编译通过,但运行抛 NotImplementedException
  • C. 编译失败,非抽象的 Circle 必须实现 Area
  • D. 编译失败,抽象方法不能声明在抽象类中
查看答案与解析

正确答案C

解析: 抽象成员必须由第一个非抽象派生类实现;Circle 既非抽象又不实现 Area,编译器直接报错。要么实现,要么继续声明为 abstract。

关键点: 抽象成员是“债务”,必须在具体类还清。

0187 以下代码输出什么?

难度: 进阶

abstract class Base
{
    public abstract int Value { get; }
}

class Derived : Base
{
    public override int Value { get; set; } = 5;
}

Console.WriteLine(new Derived().Value);
  • A. 输出 5,重写抽象属性时允许补充基类未声明的 set 访问器
  • B. 编译失败,派生类不能改变访问器集合
  • C. 输出 0
  • D. 编译失败,抽象属性必须同时声明 getset
查看答案与解析

正确答案A

解析: 重写抽象属性时,基类声明的访问器必须全部实现,但派生类可以额外补充访问器(此处补了 set);属性初始化器 = 5 也生效。

关键点: 抽象属性是“最低契约”,派生类可扩展访问器。

0188 以下代码能否编译?

难度: 实战

abstract class A
{
    public abstract void M() { }
}
  • A. 编译通过,抽象方法可以带空方法体
  • B. 编译通过,但运行抛异常
  • C. 编译失败,抽象类不能声明方法
  • D. 编译失败,抽象方法不能有实现体
查看答案与解析

正确答案D

解析: abstract 成员只能声明签名,带方法体与 abstract 语义冲突,编译器报 CS0500。

关键点: abstract 与“有实现”互斥。

0189 以下代码运行结果是什么?

难度: 实战

interface ILeft { void M(); }
interface IRight { void M(); }

class C : ILeft, IRight
{
    void ILeft.M() => Console.WriteLine("L");
    void IRight.M() => Console.WriteLine("R");
}

var c = new C();
c.M();
  • A. 输出 L
  • B. 编译失败,C 上没有公共 M,显式实现只能通过接口引用调用
  • C. 输出 R
  • D. 运行抛 AmbiguousMatchException
查看答案与解析

正确答案B

解析: 两个显式实现都“私有于接口”,类引用看不到 M;必须写成 ((ILeft)c).M()((IRight)c).M() 才能调用。

关键点: 显式接口实现默认不暴露为公共成员。

0190 以下代码能否编译?

难度: 进阶

interface I { void M(); }

class C : I
{
    public void I.M() { }
}
  • A. 编译通过
  • B. 编译通过,但运行抛异常
  • C. 编译失败,显式接口实现不能声明访问修饰符
  • D. 编译失败,接口方法必须隐式实现
查看答案与解析

正确答案C

解析: 显式接口实现的语法是 返回类型 接口名.成员,不允许加 publicprivatevirtual 等修饰符;其可见性由接口决定。

关键点: 显式实现的可见性不由修饰符控制。

0191 以下代码输出什么?

难度: 进阶

interface I
{
    void M() => Console.WriteLine("default");
}

class C : I
{
    void I.M() => Console.WriteLine("explicit");
}

var c = new C();
((I)c).M();
  • A. 输出 explicit,显式实现优先于默认实现
  • B. 输出 default
  • C. 编译失败,二者冲突
  • D. 输出 explicitdefault
查看答案与解析

正确答案A

解析: 默认实现只是“没有实现时的兜底”。类一旦提供实现(包括显式实现),通过接口调用就分发到类实现。

关键点: 显式实现同样会覆盖默认接口实现。

0192 以下代码输出什么?

难度: 实战

interface I { void M(); }

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

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

new B().M();
  • A. 编译失败,抽象类不能实现接口
  • B. 编译失败,接口成员不能映射为抽象成员
  • C. 输出 A
  • D. 输出 B,抽象类可用 abstract 成员满足接口契约,把实现下放给派生类
查看答案与解析

正确答案D

解析: 抽象类实现接口时,可以把接口成员声明为 abstract,由派生类完成实现;B 的 override 生效,输出 B

关键点: 抽象类是接口与实现之间的“延迟实现”桥梁。

0193 以下代码运行结果是什么?

难度: 进阶

interface IOp
{
    static abstract int Add(int a, int b);
}

class Calc : IOp
{
    public static int Add(int a, int b) => a + b;
}

int r = IOp.Add(1, 2);
  • A. 输出 3
  • B. 编译失败,static abstract 成员不能通过接口名直接调用,需通过受约束的类型参数或具体类型调用
  • C. 编译失败,static abstract 成员必须由 struct 实现
  • D. 运行抛 NullReferenceException
查看答案与解析

正确答案B

解析: 接口上的 static abstract 只是“声明”,没有可执行体,IOp.Add 无从解析到具体类型;必须写 Calc.Add,或在 where T : IOp 的泛型方法里写 T.Add

关键点: static abstract 的调用方是“约束下的类型参数”,不是接口名。

0194 以下代码输出什么?

难度: 实战

interface IOp
{
    static abstract int Add(int a, int b);
}

class Calc : IOp
{
    public static int Add(int a, int b) => a + b;
}

static int Sum<T>(int a, int b) where T : IOp => T.Add(a, b);

Console.WriteLine(Sum<Calc>(1, 2));
  • A. 编译失败,泛型方法不能调用 static abstract 成员
  • B. 输出 0
  • C. 输出 3,受约束的类型参数可直接调用 static abstract 成员
  • D. 编译失败,CalcAdd 必须显式实现接口成员
查看答案与解析

正确答案C

解析: where T : IOpT.Add 在编译期可用,运行时按 Calc 的具体静态实现分发。static abstract 正是为“泛型约束 + 静态分发”而设计。

关键点: static abstract 的价值在于泛型代码中按约束调用。

0195 以下代码能否编译?

难度: 进阶

interface IOp
{
    static abstract void Run();
}

class Foo : IOp { }
  • A. 编译失败,Foo 必须提供 Run 的静态实现
  • B. 编译通过,static abstract 成员可以没有实现
  • C. 编译通过,但运行抛 TypeLoadException
  • D. 编译失败,static abstract 不能声明在接口中
查看答案与解析

正确答案A

解析: static abstract 成员没有默认实现,实现类型必须给出对应的 static 成员,否则编译失败。这与普通接口成员的“必须实现”同构,但发生在静态层面。

关键点: static abstract 是“必须实现”的静态契约。

0196 关于抽象类与接口的差异,哪项正确?

难度: 进阶

  • A. 接口可以声明实例字段和构造函数,抽象类不行
  • B. 抽象类可以被结构体继承
  • C. 一个类最多只能实现一个接口
  • D. 抽象类可有实例字段与构造函数且只能被单继承,接口不能有实例状态且可被结构体实现
查看答案与解析

正确答案D

解析: 抽象类能承载状态(字段)与构造逻辑,但继承仍是单继承;接口是纯契约,不能有实例字段,struct 可实现接口但不能继承抽象类。

关键点: 选抽象类看“共享状态”,选接口看“跨层级契约”。

0197 以下代码输出什么?

难度: 进阶

interface IA
{
    void M() => Console.WriteLine("A");
}

interface IB : IA
{
    void M() => Console.WriteLine("B");
}

class C : IB { }

var c = new C();
((IA)c).M();
((IB)c).M();
  • A. 两行都输出 A
  • B. 第一行输出 A,第二行输出 B
  • C. 两行都输出 B,运行时采用“最具体覆盖”(IB 比 IA 更具体)
  • D. 编译失败,菱形冲突无法解决
查看答案与解析

正确答案C

解析: 实现类只关联最派生的接口实现:对 IA.MC 继承的是 IB 的覆盖,因此无论通过 IA 还是 IB 调用都输出 B。只有两个平级接口都提供默认实现且无最具体者时才报 CS8705。

关键点: 默认接口方法按“最具体接口覆盖”分派,不是按调用方声明的接口就近选择。

0198 以下代码输出什么?

难度: 进阶

interface I { void M(); }

class C : I
{
    public void M() => Console.WriteLine("public");
    void I.M() => Console.WriteLine("explicit");
}

var c = new C();
c.M();
((I)c).M();
  • A. 第一行输出 public,第二行输出 explicit
  • B. 两行都输出 explicit
  • C. 两行都输出 public
  • D. 编译失败,同名成员重复
查看答案与解析

正确答案A

解析: 公共实现是类的成员,类引用调用它;接口引用调用时显式实现优先。二者可以共存,形成“类入口”与“接口入口”两套行为。

关键点: 公共实现与显式实现并存时,接口入口总是显式实现。

0199 关于 static abstract 接口成员的调用方式,哪项正确?

难度: 进阶

  • A. 可以直接通过接口实例(如 ((IOp)obj).Add(1, 2))调用
  • B. 通常需要通过受该接口约束的泛型类型参数(where T : IOp)或具体实现类型调用,接口名本身不能直接调用
  • C. 声明了 static abstract 的接口不能被类实现
  • D. static abstract 成员必须由 static class 实现
查看答案与解析

正确答案B

解析: static abstract 在接口上只有声明没有实现体,可执行的调用路径是“具体类型名”或“约束类型参数”。任何“通过接口名/接口实例直接调用”的写法都无法编译。

关键点: 记不住“不能通过接口名/实例调用”,就会写出无法编译的代码。

0200 以下代码输出什么?

难度: 实战

interface IAddable<T> where T : IAddable<T>
{
    static abstract T operator +(T a, T b);
}

record struct Vec(int X, int Y) : IAddable<Vec>
{
    public static Vec operator +(Vec a, Vec b) => new(a.X + b.X, a.Y + b.Y);
}

static T Add<T>(T a, T b) where T : IAddable<T> => a + b;

Console.WriteLine(Add(new Vec(1, 2), new Vec(3, 4)));
  • A. 编译失败,泛型方法中不能使用 +
  • B. 输出 Vec { X = 1, Y = 2 }
  • C. 输出 4
  • D. 输出 Vec { X = 4, Y = 4 },约束让 a + b 解析到 static abstract 运算符
查看答案与解析

正确答案D

解析: where T : IAddable<T> 声明了 static abstract operator +,泛型代码中的 a + b 会分发到 Vec 的运算符实现;record struct 的 ToString 输出全部成员。这正是 IAdditionOperators/INumber<T> 的底层模式。

关键点: 泛型数学的根基是 static abstract 运算符 + 类型约束。

官方资料

当前分类

C# 选择题

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

输入关键词开始搜索