重载继承类中的方法

时间:2017-01-31 21:41:03

标签: c# .net clr

我已经开始明白,我不明白发生了什么。 C#中有以下行为:

public class Base
{
    public void Method(D a)
    {
        Console.WriteLine("public void Method(D a)");
    }
}

public class Derived: Base
{
    public void Method(B a)
    {
        Console.WriteLine("public void Method(B a)");
    }
}

public class B { }

public class D: B { }

class Program
{
    static void Main(string[] args)
    {
        Derived derived = new Derived();
        D d = new D();

        derived.Method(d);
    }
}

会打印

public void Method(B a)

而不是

public void Method(D a)

令人惊讶。我想这种行为的原因是方法表的实现。如果CLR在当前类型中找到相应的方法,则它不会搜索基类中的方法。我认为他们正在努力提高绩效。

但我对以下代码感到非常失望:

public class Base
{
    public virtual void Method(D a)
    {
        Console.WriteLine("public void Method(D a)");
    }
}

public class Derived: Base
{
    public override void Method(D a)
    {
        Console.WriteLine("public override void Method(D a)");
    }

    public void Method(B a)
    {
        Console.WriteLine("public void Method(B a)");
    }

}

public class B { }

public class D: B { }

class Program
{
    static void Main(string[] args)
    {
        Derived derived = new Derived();
        D d = new D();

        derived.Method(d);
    }
}

它会打印

public void Method(B a)

而不是

public override void Method(D a)

这太糟糕了,非常难以预测。

任何人都可以解释一下吗?

我认为方法表只有在当前类型中实现的方法(不包括重写方法),并且只要找到任何可以调用的方法,CLR就会停止查找相应的方法。我是对的吗?

2 个答案:

答案 0 :(得分:15)

  

我已经开始明白我不明白发生了什么。

因此开始了智慧。

  

这太糟糕了,非常难以预测。

它既不是。相比之下,该功能旨在通过消除脆弱基类失败的原因来减少不可预测性。

  

任何人都可以解释一下吗?

请参阅我2007年关于此主题的文章。

https://blogs.msdn.microsoft.com/ericlippert/2007/09/04/future-breaking-changes-part-three/

简短版本是:派生类中的方法总是比基类中的方法更好;编写派生类的人更多地了解对象语义并且处理更具体的案例而不是编写基类的人。

Servy指出,我没有说明你的第二点。为什么派生类中的覆盖不会使“D”方法“在派生类中”?

虚拟方法被认为是声明的类的方法,而不是最近被覆盖的类的 。为什么?因为选择是否覆盖是类的实现细节,而不是公共表面区域的一部分。

我的意思是,考虑一下。你写了一些代码,它可以工作,然后你说,嘿,我将在这个类型层次结构中创建一个新的(或删除旧的!)覆盖此方法某处,并突然间在其他地方更改重载决议?这正是C#试图消除的那种脆弱性。

请记住,C#是一个非常精心设计的,适用于人员编辑代码的世界。奇怪的是,许多现代语言被设计成好像一个人正在编写所有代码并且他们第一次就做对了;那是不现实的。 C#的一些更不寻常的功能,就像你发现的一样,可以帮助你保持程序行为的可预测性,即使其他人正在编辑你的基类。

答案 1 :(得分:1)

我可以看到这里的混乱。通过一个例子可以更好地说明Eric的答案。如果您更改了以下代码...

public class Base
{
    public virtual void Method(D a)
    {
        Console.WriteLine("public void Method(D a)");
    }

    public void Method(B a)
    {
        Console.WriteLine("public void Method(B a)");
    }
}

public class Derived : Base
{
    public override void Method(D a)
    {
        Console.WriteLine("public override void Method(D a)");
    }

}

public class B { }

public class D : B { }

输出变为“public override void Method(D a)”。

为什么?

如Eric的文章中所述......

  如果派生中的任何方法,基类中的

方法不是候选方法   课程适用

由于方法(D)和方法(B)现在都在基类中,因此方法(D)已成为最接近的匹配,它在派生类中被覆盖,因此输出。

对新手来说相当困惑嘿?对于经验丰富的开发人员来说也很困惑。我认为这里要做的关键点是你在这里有什么不好的设计,我不是在谈论CLR或C#编译器,它做得最好它可以帮助你在这里,我在谈论类在你的例子中。

我认为很多人对OOP不熟悉的一个常见错误就是过度使用继承的新玩具。继承通常不是OOD中使用的优惠关系。当您开始研究OOP设计模式时,您会注意到许多模式不使用继承。

为什么呢?因为您的示例说明了在使用继承时可以创建的混淆,所以它被称为Fragile Inheritance。设计模式更经常使用聚合和组合将问题空间划分为更可行,可扩展和可管理的解决方案。

继承是一种强大的构造,与大多数强大的东西一样,它应该谨慎使用。