请教一个关于多态的练习题

时间:2021-08-14 12:23:05
class A{
public string Str = "A";
public void Show() { Console.WriteLine("Show A"); }
}
class B : A{
public string Str = "B";
public virtual void Show() { Console.WriteLine("Show B"); }
}
class C : B{
public override void Show() { Console.WriteLine("Show C"); }
}
class D : C{
public string Str = "D";
public void Show() { Console.WriteLine("Show D"); }
}
class Program{
static void Main(string[] args) {
D d = new D();
C c = d;
B b = d;
A a = d;
Console.WriteLine(d.Str);
Console.WriteLine(c.Str);
Console.WriteLine(b.Str);
Console.WriteLine(a.Str);
Console.WriteLine("------------");
d.Show(); 
c.Show(); 
b.Show(); 
a.Show(); 
Console.ReadLine();
}
}


题的答案根据“new则隐藏,over重写;隐藏看类型,重写只管新”这个口诀大概能答的出来,但是不太理解
1.关于隐藏
在VS中,提示D.Show()将隐藏继承的成员C.Show(),但是在C c = d;,这个时候C.Show()不是应该被隐藏了么?
2.b.Show();  这里答案为什么是Show C
里氏转换的时候B b = d;到底经历了哪些过程?为何class C中的override会在转换中起作用。

6 个解决方案

#1


对于隐藏
d.Show(); 和((C)d).Show(); 运行下

#2


1、这就是new会造成的问题,跟当前类或接口是什么有关
2、不要盲目的加上里氏转换,你这个D就是不符合替换原则,至于为什么是C,因为到C为止是符合替换原则的……

#3


看起来晕头转向的,商业项目估计没这么玩的
单纯的从语法解释,有点隔靴搔痒,你看下IL,看编译器做了神马事情

#4


引用 2 楼 starfd 的回复:
1、这就是new会造成的问题,跟当前类或接口是什么有关
2、不要盲目的加上里氏转换,你这个D就是不符合替换原则,至于为什么是C,因为到C为止是符合替换原则的……
关于“替换原则”没有学习过,能否提供下资料或者讲解下。

#5


你这里的a、b、c、d都是引用同一个对象。

首先要说明的是,c#的这种new写法(它显然是从c++中学来的)是个“糟粕”。它直接破坏了面向对象基本原则。虽然vs只是给你警告而没有禁止,我们无法因此就不用vs开发工具,但是它的危害还是要了解清楚的。

比如说你可以修改一下代码,看得更清楚
class B : A
{
    public double Str = 1234.5678;
    public virtual void Show() { Console.WriteLine("Show B"); }
}

这里的Str 跟继承性毫无关系,是明明知道有命名冲突、而又故意不换其它命名,硬要偷换Str的概念。如果vs不允许程序员这样来胡写new,而是只能另外命名为与Str不冲突的变量名,那么程序诈误就少了,关键地就可以更好地运用继承和多态的本来的意思而不被粗暴地偷换命名了。

这种问题根本不是面向对象设计技术的问题,而是破坏了面向对象原则所产生的问题。先要知道这个关键点,就容易回答问题。

1. obj.Show()方法接口是从A类型继承的。只要你声明了类型为C的变量c,它就具有.Show()方法。vs警告中所谓“隐藏”这个说法不准确,更准确的说法应该是“偷换”。但是虽然D偷换了C的Show()方法的含义,但是D从C所继承的方法依然存在。你可以执行
((B)d).Show();
((C)d).Show();

可以看到以B、C类多态方式执行这个对象(4个变量引用同一个d类对象实例)的Show()方法还是打印“C”,只不过以D的偷换概念方式来调用Show()方法时才打印“D"。所以”隐藏“的含义是运行时去偷换,不是在编译时就明着把它删除了。

2. 这个变量b引用的是一个D类型对象实例。如果找这个对象的B类型接口中的Show方法,那么最新的重写就是C.Show()方法。而D.Show只是与重写B.Show方法定义毫无关系另外一个方法(只不过方法命名冲突),因此不会被调用。

override就是重写,是符合面向对象设计原则的。子类不但可以继承,也可以重写继承的虚方法。如果只能继承而不能重写,那就是结构化方法了。面向对象的最关键的一点就是支持override。

但是new是糟粕,它允许你用另外一个毫无关系的Str和Show来混淆继承甚至重写功能,在读代码时很容易误解。所以如果这种代码存在,就应该立刻删掉。只应该允许符合继承的Str字段,和符合继承和重写规则的Show方法。

#6


如果只能继承而不能重写,那就是结构化方法了  --〉  如果一个编程语言体系中只能继承而不能重写,那就只是一种结构化方法了

“即可以继承,同时又可以重写”,这才是面向对象方法的关键。如果总是很容易理解继承,但是就是不理解重写,甚至担心重写(规范地重写)会带来混乱,那么就会用一大堆组合属性来代替继承,否则就不能实现灵活的扩展需求了。但使用一大堆组合属性,可能又要编出一大堆附属的笨拙的结构化模式来实现原本用重写而轻松扩展的功能。

#1


对于隐藏
d.Show(); 和((C)d).Show(); 运行下

#2


1、这就是new会造成的问题,跟当前类或接口是什么有关
2、不要盲目的加上里氏转换,你这个D就是不符合替换原则,至于为什么是C,因为到C为止是符合替换原则的……

#3


看起来晕头转向的,商业项目估计没这么玩的
单纯的从语法解释,有点隔靴搔痒,你看下IL,看编译器做了神马事情

#4


引用 2 楼 starfd 的回复:
1、这就是new会造成的问题,跟当前类或接口是什么有关
2、不要盲目的加上里氏转换,你这个D就是不符合替换原则,至于为什么是C,因为到C为止是符合替换原则的……
关于“替换原则”没有学习过,能否提供下资料或者讲解下。

#5


你这里的a、b、c、d都是引用同一个对象。

首先要说明的是,c#的这种new写法(它显然是从c++中学来的)是个“糟粕”。它直接破坏了面向对象基本原则。虽然vs只是给你警告而没有禁止,我们无法因此就不用vs开发工具,但是它的危害还是要了解清楚的。

比如说你可以修改一下代码,看得更清楚
class B : A
{
    public double Str = 1234.5678;
    public virtual void Show() { Console.WriteLine("Show B"); }
}

这里的Str 跟继承性毫无关系,是明明知道有命名冲突、而又故意不换其它命名,硬要偷换Str的概念。如果vs不允许程序员这样来胡写new,而是只能另外命名为与Str不冲突的变量名,那么程序诈误就少了,关键地就可以更好地运用继承和多态的本来的意思而不被粗暴地偷换命名了。

这种问题根本不是面向对象设计技术的问题,而是破坏了面向对象原则所产生的问题。先要知道这个关键点,就容易回答问题。

1. obj.Show()方法接口是从A类型继承的。只要你声明了类型为C的变量c,它就具有.Show()方法。vs警告中所谓“隐藏”这个说法不准确,更准确的说法应该是“偷换”。但是虽然D偷换了C的Show()方法的含义,但是D从C所继承的方法依然存在。你可以执行
((B)d).Show();
((C)d).Show();

可以看到以B、C类多态方式执行这个对象(4个变量引用同一个d类对象实例)的Show()方法还是打印“C”,只不过以D的偷换概念方式来调用Show()方法时才打印“D"。所以”隐藏“的含义是运行时去偷换,不是在编译时就明着把它删除了。

2. 这个变量b引用的是一个D类型对象实例。如果找这个对象的B类型接口中的Show方法,那么最新的重写就是C.Show()方法。而D.Show只是与重写B.Show方法定义毫无关系另外一个方法(只不过方法命名冲突),因此不会被调用。

override就是重写,是符合面向对象设计原则的。子类不但可以继承,也可以重写继承的虚方法。如果只能继承而不能重写,那就是结构化方法了。面向对象的最关键的一点就是支持override。

但是new是糟粕,它允许你用另外一个毫无关系的Str和Show来混淆继承甚至重写功能,在读代码时很容易误解。所以如果这种代码存在,就应该立刻删掉。只应该允许符合继承的Str字段,和符合继承和重写规则的Show方法。

#6


如果只能继承而不能重写,那就是结构化方法了  --〉  如果一个编程语言体系中只能继承而不能重写,那就只是一种结构化方法了

“即可以继承,同时又可以重写”,这才是面向对象方法的关键。如果总是很容易理解继承,但是就是不理解重写,甚至担心重写(规范地重写)会带来混乱,那么就会用一大堆组合属性来代替继承,否则就不能实现灵活的扩展需求了。但使用一大堆组合属性,可能又要编出一大堆附属的笨拙的结构化模式来实现原本用重写而轻松扩展的功能。