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 未声明 Paint,w.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重新抽象了M,C必须提供实现 - 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. 编译失败,抽象属性必须同时声明
get和set
查看答案与解析
正确答案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
解析: 显式接口实现的语法是 返回类型 接口名.成员,不允许加 public、private、virtual 等修饰符;其可见性由接口决定。
关键点: 显式实现的可见性不由修饰符控制。
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. 编译失败,
Calc的Add必须显式实现接口成员
查看答案与解析
正确答案C
解析: where T : IOp 让 T.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.M,C 继承的是 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 运算符 + 类型约束。