微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C#8.0默认接口成员的意外行为

如何解决C#8.0默认接口成员的意外行为

考虑以下代码

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中预期的,还是某些错误的结果?如果需要,为什么C中的方法仅在声明为虚拟时(对于I而不是M2)并且仅当声明为虚拟时才隐式实现M1的相应成员{1}}被声明为抽象吗?

编辑:

虽然我仍然不清楚这是错误还是功能(我倾向于认为这是错误,并且到目前为止,第一条评论中的讨论尚无定论),但我提出了一个更加危险的选择情况:

A

请注意,接口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对于各自的类是 private 。特别是,方法Program.C应该从类Program.C.Method外部不可访问。类Program的作者可能认为完全控制何时调用方法Program,甚至可能不知道接口Program.C.Method(因为它是私有的)。但是,它从Library.I中被调用,因为输出为:

Library.A.OtherMethod

这看起来像是一种脆性的基类问题。 Program.C.Method 被宣布为公开的事实应该是无关紧要的。参见埃里克·利珀特(Eric Lippert)的this blog post,其中描述了一种不同但有些相似的情况。

解决方法

自从C#8.0引入以来,支持接口的默认实现。通过此介绍,已更改了接口的实现成员的查找过程。关键部分在于如何定义实例(在您的示例obj中)或类型语法。

让我们从进行成员解析的7.3种方法开始,并将I obj = new C();替换为C obj = new C();。运行此命令时,将输出以下输出: C.M1 C.M2

您可以看到两个WriteLine都将结果打印为类C定义的实现。这是因为类型语法引用了一个类,而“ first in line”实现是类C的实现。

现在,当我们将其更改回I obj = new C();时,会看到不同的结果,即: I.M1 C.M2 这是因为虚拟成员和抽象成员不会像M1(未标记为虚拟)那样被大多数派生的实现所取代。

现在主要问题仍然存在,为什么C中的方法仅在声明为虚时(对于M2而不是M1时)并且仅在将A声明为抽象时才隐式实现I的相应成员?

当类A是一个非抽象类时,它是“主动”实现接口,而当它是抽象类时,该类只需要在其中插入抽象类的类也实现接口。 当我们查看您的示例时,我们不能这样写:|

A obj = new C();

System.Console.WriteLine(obj.M1()); // Method M1() is not defined

有关更多信息,请点击此处:https://github.com/dotnet/roslyn/blob/master/docs/features/DefaultInterfaceImplementation.md

以下是其结果的一些变化:

I obj = new C(); // with A as abstract class 结果是 I.M1 C.M2

I obj = new C(); // with A as class 结果是 I.M1 I.M2

C obj = new C(); // with or without A as abstract class 结果是 C.M1 C.M2

I obj = new A(); // with A as class 结果是 I.M1 I.M2

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。