Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 说明规范为何良好的理由_C#_Inheritance_Overriding - Fatal编程技术网

C# 说明规范为何良好的理由

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(长颈鹿)时,我们应该期望超负荷解决

假设我们有基类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上调用这个方法,所以特异性应该是赢家。接收器的特异性远比其任何参数的特异性更为重要参数类型用于断开连接。重要的是确保我们选择最具体的接收器,因为该方法是由更了解D打算处理的场景的人稍后编写的

现在,你可能会说,如果D的作者也推翻了B.M(长颈鹿),那该怎么办?在这种情况下,为什么对D.M(长颈鹿)的调用应该调用D.M(动物)有两个理由

首先,《D》的作者应该知道,D.M(动物)可以与长颈鹿一起被称为“D.M”,而且它必须写为“做正确的事情”。因此,从用户的角度来看,将调用解析为D.M(动物)还是B.M(长颈鹿)并不重要,因为D已正确写入以执行正确的操作


其次,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);
  }