![](/img/trans.png)
[英]In C# 8.0 is there a way to expose default member implementation on the class?
[英]Unexpected behavior of a C# 8.0 default interface member
考虑以下代码:
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 中产生以下意外的 output:
I.M1
C.M2
Class A
没有I
成员的隐式或显式实现,所以我希望默认实现用于C
,因为C
继承了A
的接口映射并且没有明确地重新实现I
。 根据 ECMA-334 (18.6.6) 和 C# 6.0 语言规范:
class 继承了其基类提供的所有接口实现。
如果不显式重新实现接口,派生的 class 不能以任何方式改变它从其基类继承的接口映射。
特别是,我期望以下 output:
I.M1
I.M2
当A
未声明为抽象时,确实会发生这种情况。
上面代码的行为是 C# 8.0 中的预期行为,还是某些错误的结果? 如果有意,为什么C
中的方法仅在声明为虚拟时(在M2
而不是M1
的情况下)并且仅在A
声明为抽象时才隐式实现I
的相应成员?
编辑:
虽然我仍然不清楚这是一个错误还是一个功能(我倾向于认为这是一个错误,并且第一条评论中链接的讨论到目前为止还没有定论),但我想到了一个更危险的场景:
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
和 class Program.C
是各自类的私有接口。 特别是,方法Program.C.Method
应该无法从 class Program
外部访问。 class Program
的作者可能认为可以完全控制方法Program.C.Method
何时被调用,甚至可能不知道接口Library.I
(因为它是私有的)。 但是,它是从Library.A.OtherMethod
调用的,因为 output 是:
Program.C.Method
这看起来像是一种脆弱的基础 class 问题。 Program.C.Method
被声明为公开的事实应该是无关紧要的。 请参阅 Eric Lippert 的这篇博客文章,其中描述了一个不同但有些相似的场景。
自从引入 C# 8.0 以来,支持接口的默认实现。 通过此介绍,接口的实现成员的查找过程已更改。 关键部分在于如何定义实例(在您的示例 obj 中)或类型语法。
让我们从执行成员解析的 7.3 方法开始,并替换I obj = new C();
C obj = new C();
运行时将打印以下 output: C.M1 C.M2
如您所见,WriteLine 都将结果打印为 class C 定义的实现。这是因为类型语法指的是 class,而“第一行”实现是 class C 的实现。
现在当我们把它改回I obj = new C();
我们看到不同的结果,即: I.M1 C.M2
这是因为虚拟和抽象成员没有像 M1(未标记为虚拟)那样被最派生的实现替换。
现在主要问题仍然存在,为什么 C 中的方法仅在声明为虚拟时(在 M2 而不是 M1 的情况下)并且仅在 A 声明为抽象时才隐式实现 I 的相应成员?
当 class A 是非抽象 class 时,它“主动”实现接口,而当它是抽象 class 时,class 仅需要继承接口 class 的 class 也实现接口 88399581818。 当我们查看您的示例时,我们不能这样写:|
A obj = new C();
System.Console.WriteLine(obj.M1()); // Method M1() is not defined
有关更多信息,您可以在这里查看: https://github.com/do.net/roslyn/blob/master/docs/features/DefaultInterfaceImplementation.md
以下是他们的结果的一些变化:
I obj = new C(); // with A as abstract class
I obj = new C(); // with A as abstract class
结果为I.M1 C.M2
I obj = new C(); // with A as class
I obj = new C(); // with A as class
结果为I.M1 I.M2
C obj = new C(); // with or without A as abstract class
C obj = new C(); // with or without A as abstract class
结果为C.M1 C.M2
I obj = new A(); // with A as class
I obj = new A(); // with A as class
结果为I.M1 I.M2
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.