考虑以下代码:
interface I {
string M1() => "I.M1";
string M2() => "I.M2";
}
abstract class A : I {}
class C : A {
public string M1() => "C.M1";
public virtual string M2() => "C.M2";
}
class Program {
static void Main() {
I obj = new C();
System.Console.WriteLine(obj.M1());
System.Console.WriteLine(obj.M2());
}
}
在.NET Core 3.1.402中,它会产生以下意外输出:
I.M1
C.M2
类 A
没有实现接口成员 I
的隐式或显式实现,因此我预期默认实现将用于 C
,因为 C
继承了 A
的接口映射并且没有显式重新实现 I
。根据 ECMA-334 (18.6.6) 和 C# 6.0 语言规范:
一个类继承其基类提供的所有接口实现。
未显式 重新实现 接口的情况下,派生类无法以任何方式更改它从基类继承的接口映射。
特别是,我预期以下输出:
I.M1
I.M2
如果没有将A
声明为抽象类,就会发生这种情况。
在C# 8.0中,上面的代码行为是有意的吗,还是某个bug的结果?如果是有意的,为什么只有当M2
被声明为虚拟的(对于M1
不是)并且A
被声明为抽象时,C
中的方法才隐式实现相应的I
成员?
编辑:
虽然我仍然不确定这是一个bug还是一个feature(我倾向于认为这是一个bug,而第一条评论中链接的讨论至今没有得出结论),但我想到了一个更加危险的场景:
class Library {
private interface I {
string Method() => "Library.I.Method";
}
public abstract class A: I {
public string OtherMethod() => ((I)this).Method();
}
}
class Program {
private class C: Library.A {
public virtual string Method() => "Program.C.Method";
}
static void Main() {
C obj = new C();
System.Console.WriteLine(obj.OtherMethod());
}
}
注意接口 Library.I
和类 Program.C
分别对它们所在的类是私有的。特别地,方法 Program.C.Method
应当无法从类 Program
外部访问。 类 Program
的作者可能认为完全掌控了何时调用方法 Program.C.Method
,甚至可能不知道接口 Library.I
(因为它是私有的)。然而,Library.A.OtherMethod
中会调用该方法,输出如下:
Program.C.Method
这看起来像是一种脆弱基类的问题。 Program.C.Method
声明为公共的事实应该是无关紧要的。请参阅 Eric Lippert 的这篇博客文章,其中描述了一个不同但有些相似的情况。