C# 说明规范为何良好的理由
假设我们有基类B和派生类D。B有一个方法M,它采用Giraffe。现在,请记住,根据假设,D的作者了解B的公共成员和受保护成员的一切。换句话说:D的作者必须比B的作者了解更多,因为D是在B之后编写的,而D是为了将B扩展到B尚未处理的场景。因此,我们应该相信D的作者在实现D的所有功能方面比B的作者做得更好 如果《D》的作者对一只动物做了大量的研究,他们会说我比《B》的作者更了解如何对待动物,这包括长颈鹿。当给D.M(长颈鹿)打电话叫D.M(动物),而不是B.M(长颈鹿)时,我们应该期望超负荷解决 换一种说法:我们有两种可能的理由:C# 说明规范为何良好的理由,c#,inheritance,overriding,C#,Inheritance,Overriding,假设我们有基类B和派生类D。B有一个方法M,它采用Giraffe。现在,请记住,根据假设,D的作者了解B的公共成员和受保护成员的一切。换句话说:D的作者必须比B的作者了解更多,因为D是在B之后编写的,而D是为了将B扩展到B尚未处理的场景。因此,我们应该相信D的作者在实现D的所有功能方面比B的作者做得更好 如果《D》的作者对一只动物做了大量的研究,他们会说我比《B》的作者更了解如何对待动物,这包括长颈鹿。当给D.M(长颈鹿)打电话叫D.M(动物),而不是B.M(长颈鹿)时,我们应该期望超负荷解决
- 打给D.M(长颈鹿)的电话应该打给B.M(长颈鹿),因为长颈鹿比动物更具特异性
- 打给D.M(长颈鹿)的电话应该打给D.M(动物),因为D比B更具体
其次,D的作者是否重写了B的方法是D的一个实现细节,而不是公共表面积的一部分。换句话说:如果更改方法是否被覆盖会更改选择的方法,这将非常奇怪。想象一下,如果您在一个版本中对某个基类调用一个方法,然后在下一个版本中,基类的作者对是否重写某个方法进行了微小的更改;您不会期望派生类中的重载解析发生更改。C#经过精心设计,以防止此类故障。我几天前读过同样的问题,但再也找不到了。主要的一点是,编译器首先检查匹配的实例方法。被重写的方法仍然可以由基类声明,因此只有在
Derived
中没有声明匹配的方法,但是Foo(object)
确实匹配时,才会考虑它们。Jon Skeet(或者Eric Lippert)有一篇关于这一点的博客,仍在努力寻找答案it@RenéVogt:你确定是编译器检查它还是运行时检查它?@TimSchmelter是的,编译器。找到Jon Skeet的博客:向下滚动到“继承”。@RenéVogt:我不认为这很简单:@TimSchmelter但这不重要,我们不讨论虚拟方法,因为编译器已经选择使用非虚拟实例方法Foo(object)
。如果这不存在,它将选择虚拟方法,然后您链接的事情将在运行时发生。老实说,我不理解规范如何适用于OP的情况,因为派生的
显式覆盖Foo(int x)
。因此Foo(int)
与Foo(object)
一样接近,以便在d.Foo(i)
调用中解决重载问题。如果Derived
没有重写基类方法,我会同意,但这里不是这样。埃里克·利珀特(Eric Lippert)的澄清确实适用,因为它明确提到了“首次声明”。但这似乎并没有被规范所证实,至少他的“脆弱的基类”理论并没有真正证明这一点。@KonradRudolph:一个类是否重写一个方法是该类的一个实现细节。你的建议基本上是,如果我改变一个类是否重写了一个方法,这会改变重载解析的工作方式,这是一个脆弱的基类失败!这样考虑:假设我有类A,B,C,D,其中D:C,C:B,B:A。假设A声明了一个虚方法M,C重写了它。你是说如果我将覆盖从C移到B,D.M的重载解析可能会改变。这是一个糟糕的用户体验。@KonradRudolph:现在,你的观点“我不明白规范是如何应用的”是正确的,这是因为Saruman引用了规范中错误的部分。那是关于方法调用的运行时处理的部分。问题是关于编译时重载的解决方法。@Saruman,您应该引用的规范部分是读取候选方法集的部分,该部分被缩减为只包含来自最派生类型的方法:对于集合中的每个方法C.F,其中C是声明方法F的类型,将从集合中删除C基类型中声明的所有方法。请注意,正如康拉德•鲁道夫所指出的,C++中的“声明”是重要的一部分。首先,C++是为了解决C++的设计缺陷,这是其中之一。其次,根据我的经验,当你从基类派生出C++程序的所有源代码时,这是很常见的。在C#中,从库中的基类派生是非常常见的,因为库中的所有
using System;
class Base
{
public virtual void Foo(int x)
{
Console.WriteLine("Base.Foo(int)");
}
}
class Derived : Base
{
public override void Foo(int x)
{
Console.WriteLine("Derived.Foo(int)");
}
public void Foo(object o)
{
Console.WriteLine("Derived.Foo(object)");
}
}
public class Program
{
public static void Main()
{
Derived d = new Derived();
int i = 10;
d.Foo(i);
}
}
Derived.Foo(object)
public interface ITest
{
void Foo(int x);
}