有人可以解释同一类上的相同函数的行为有何不同吗?
using System;
public class HelloWorld
{
public static void Main(string[] args)
{
MyUpperScript mAsdfScript = new MyUpperScript();
IInterface mAsdfInterface = mAsdfScript;
mAsdfScript.Foo();
mAsdfInterface.Foo();
}
}
public class MyUpperScript : MyScript
{
public void Foo()
{
Console.WriteLine("Upper Script");
}
}
public class MyScript : IInterface
{
}
public interface IInterface
{
public void Foo()
{
Console.WriteLine("Interface");
}
}
我期待它在 Foo() 调用上都写“Upper Script”
请注意,如果我也从接口派生第二个类,它会按照我期望的方式工作 - 都打印“Upper Script”:
// now deriving for the interface too:
public class MyUpperScript : MyScript, IInterface
{
public void Foo()
{
Console.WriteLine("Upper Script");
}
}
我怀疑这是因为
类不会从其接口继承成员
(来自 here),意味着
MyScript
实际上没有方法 Foo
可供 MyUpperScript
覆盖。当(现在我真的在这里猜测......!)您将变量重新转换为接口类型时,虚拟方法查找不再知道在 Foo
中查找 MyUpperScript
,因此会退回到默认实现。
不过,这对我来说仍然很奇怪,它并没有推断出
MyUpperScript
间接实现了 IInterface
- 但这可能只是我关于它如何工作的心理模型一直是错误的,而且没有机会这样做直到您拥有默认的接口成员为止,以产生任何实际的变化。 (在这些之前,隐式实现的接口的所有成员也将从显式实现的超类继承。)就我个人而言,我认为如果修复了这个问题,那么该语言可能会更有意义,以便这两个原因的行为就像您指定了接口一样明确地(如在你的尾注中),但我无法找到任何文本来向我表明这是否是一个有意识的决定,以及如果它确实是有意识的,其理由是什么。
如果您尝试直接在
MyScript
上使用它(如下所示),您应该会收到编译器错误:
MyScript myScript = new MyScript();
myScript.Foo(); // compiler error here
我认为您的代码在功能上等同于以下内容:
public class MyUpperScript : MyScript
{
public void Foo()
{
Console.WriteLine("Upper Script");
}
}
public class MyScript : IInterface
{
void IInterface.Foo()
{
Console.WriteLine("Interface");
}
}
public interface IInterface
{
void Foo();
}
请注意,
MyScript
显式实现了IInterface.Foo()
。因此 Foo
上没有 MyScript
实现,因此 MyUpperScript
可以自由地实现自己的 Foo()
。
此行为是设计使然,这样默认接口成员的行为与抽象接口成员一致。考虑您的示例的这种变体:
public class HelloWorld
{
public static void Main(string[] args)
{
MyUpperScript mAsdfScript = new MyUpperScript();
IInterface mAsdfInterface = mAsdfScript;
mAsdfScript.Foo();
mAsdfInterface.Foo();
}
}
public class MyUpperScript : MyScript
{
public new void Foo() => Console.WriteLine("Upper Script");
}
public class MyScript : IInterface
{
public void Foo() => Console.WriteLine("MyScript");
}
public interface IInterface
{
public void Foo();
}
输出是相同的:
Upper script
MyScript
首先,请注意
new
上需要 MyUpperScript.Foo()
修饰符。即使 MyScript
显式声明它实现 IInterface
,也需要 new
修饰符。这是因为接口成员默认情况下不是 virtual
。 MyScript.Foo()
不需要 override
关键字,因为它不会重写任何方法;它正在实现一个接口。
添加
IInterface.Foo()
的默认实现不会更改任何这些规则。如果您添加 IInterface.Foo()
的默认实现,但仍保留 MyScript
中的实现,编译器会警告您应该在 new
上添加 MyUpperScript.Foo()
修饰符。如果没有实现 MyScript.Foo()
,MyUpperScript.Foo()
将不再发出警告。它实现了接口成员。
这种行为确保了今天的设计目标得到满足: