C#多态怎么理解 ???

舌尖上的中国001 2018-01-15 04:53:05
本人代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pm1_15
{
class Program
{
static void Main(string[] args)
{
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
//父类的引用指向子类的对象
Console.WriteLine("1----" + a1.show(b));//A and A
Console.WriteLine("2----" + a1.show(c));//A and A
Console.WriteLine("3----" + a1.show(d));//A and D
Console.WriteLine("4----" + a2.show(b));//B and A
Console.WriteLine("5----" + a2.show(c));//B and A
Console.WriteLine("6----" + a2.show(d));//A and D
Console.WriteLine("7----" + b.show(b));// B and B
Console.WriteLine("8----" + b.show(c));// B and B
Console.WriteLine("9----" + b.show(d));// B and B


Console.ReadLine();
}
}

class A
{
public string show(D obj)
{
return ("A and D");
}
public virtual string show(A obj)
{
return ("A and A");
}
}
class B : A
{
public string show(B obj)
{
return ("B and B");
}
public override string show(A obj)
{
return ("B and A");
}
}
class C : B { }
class D : B { }
}
求分析每一个输出的原因。(注释的是输出的结果——一个都看不懂)
这里父类的引用指向子类的对象怎么理解?=大神别分析堆栈!!!看不懂,我只是一个自学的人
...全文
1019 38 打赏 收藏 转发到动态 举报
写回复
用AI写文章
38 条回复
切换为时间正序
请发表友善的回复…
发表回复
lkf181 2018-01-17
  • 打赏
  • 举报
回复
定义一个始人类祖类 person 里面有work方法,而不同的人类继承自person 同样有work方法,而不同的人类的work方法会有不同的动作,比如:厨师的work方法是做饭,教师的work是批评学生 .............
l_yfdashen 2018-01-17
  • 打赏
  • 举报
回复
女人跟男人都是人
xuzuning 2018-01-17
  • 打赏
  • 举报
回复
其实是你自己把自己弄糊涂了,把你的代码改了一下,加了点提示
        static void Main(string[] args)
{
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
//父类的引用指向子类的对象
Console.WriteLine("1--a1.show(b)--" + a1.show(b));//A and A
Console.WriteLine("2--a1.show(c)--" + a1.show(c));//A and A
Console.WriteLine("3--a1.show(d)--" + a1.show(d));//A and D
Console.WriteLine("4--a2.show(b)--" + a2.show(b));//B and A
Console.WriteLine("5--a2.show(c)--" + a2.show(c));//B and A
Console.WriteLine("6--a2.show(d)--" + a2.show(d));//A and D
Console.WriteLine("7--b.show(b)--" + b.show(b));// B and B
Console.WriteLine("8--b.show(c)--" + b.show(c));// B and B
Console.WriteLine("9--b.show(d)--" + b.show(d));// B and B


Console.ReadLine();
}
}

class A
{
public string show(D obj)
{
//return ("A and D");
return string.Format("A and D :{0}({1})", this.GetType().Name, obj.GetType().Name);
}
public virtual string show(A obj)
{
//return ("A and A");
return string.Format("A and A :{0}({1})", this.GetType().Name, obj.GetType().Name);
}
}
class B : A
{

public string show(B obj)
{
return string.Format("B and B :{0}({1})", this.GetType().Name, obj.GetType().Name);
}
public override string show(A obj)
{
return string.Format("B and A :{0}({1})*", this.GetType().Name, obj.GetType().Name);
}

}

形如 A(B) 表示的是:当前的(传入的类),带 * 的表示执行了重写的 show 方法
  • 打赏
  • 举报
回复
其实看这里的汇编,并不困难。对于任何东西你深入一点点,都会有收获。比如说
IL_007c: nop
IL_007d: ldstr "5----"
IL_0082: ldloc.1
IL_0083: ldloc.3
IL_0084: callvirt instance string ConsoleApp1.A::show(class ConsoleApp1.A)
IL_0089: call string [mscorlib]System.String::Concat(string, string)
IL_008e: call void [mscorlib]System.Console::WriteLine(string)
IL_0093: nop
IL_0094: ldstr "6----"
IL_0099: ldloc.1
IL_009a: ldloc.s d
IL_009c: callvirt instance string ConsoleApp1.A::show(class ConsoleApp1.D)
IL_00a1: call string [mscorlib]System.String::Concat(string, string)
IL_00a6: call void [mscorlib]System.Console::WriteLine(string)
IL_00ab: nop
这其实对着你的 c# 代码,是很好的参考。并不需要你会用汇编编程,这里会“猜”就行了。
  • 打赏
  • 举报
回复
如果你学习结构化软件工程,你的思维方式和设计完全改为面向对象的系统分析和设计方式,并且保证所写出的代码跟分析一致(许多人是说的一套OOA,写代码就完全忘记了OOD而编程仅仅结构化了),基本上熟练使用概念又不冗余地来进行设计,往往需要5年以上时间。
xiaoxiangqing 2018-01-17
  • 打赏
  • 举报
回复
跟c++的差不多,但有一些细微的差别,实现原理基本上一样
正怒月神 2018-01-17
  • 打赏
  • 举报
回复
上面各位都已经解释的很好了。 其实我觉得既然楼主是新人,那么可以慢慢来。 对于多态,继承这些概念,前期只要了解一下概念。 这些都是要通过后台不断接触,尝试才能慢慢理解的。 因为牵涉到这两块的话,基本上离自己搭建框架也不远了。
sp1234_maJia 2018-01-17
  • 打赏
  • 举报
回复
sorry,上面 #29楼还是针对 a2.show(c) 代码说明的面向对象机制。 对于 a2.Show(d) 来说,当然还是从 A 类型上找到第一个函数最匹配,然后运行时从 B 类型上开始查找这个 show 方法,B类型没有重写,于是就只是执行了basetype 即 A 类上的第一个函数!
  • 打赏
  • 举报
回复
引用 27 楼 wkqwe 的回复:
无论他override多少级,主要看你右边new了谁,是让谁做。 ?
按照协议来调用show方法,这是看a2变量(左边)声明,而不是看右边。 结构化的方式,总是比较机械,不能从抽象功能角度考虑。而面向对象的分析和编程实现,就好像“一层窗户纸”一样,你要多一点悟性把合同协定上定的标准(A类型的a2变量)跟实际执行人(B类型的对象实例)统一起来理解,就行了。
  • 打赏
  • 举报
回复
比如说代码
void test(A obj)
{
    ..........
}
这里过程 test 用来处理某个任务,定义为以兼容A类型的对象为输入参数,那么test方法说明我们实现了某种应用程序使用 obj 的操作流程,但是这个流程其实可以在不改变 test 代码的前提下随时改变运行细节的,这就是靠为 obj 参数传入兼容于A 类型的子类型的对象实例来扩展/修改 test 代码。
  • 打赏
  • 举报
回复
引用 27 楼 wkqwe 的回复:
但是我分析a2.show(d)应该输出B类中的show(B obj)即B and B
a2 变量声明为 A 类型,所以编译器会调用 A 类型中最合适的 show 方法,也就是第二个方法
        public virtual string show(A obj)
        {
            return ("A and A");
        }
这里是体现了重载的技术。 运行时,执行 callvirt 机制,会根据 this 对象的实际类型开始查找 show 实际方法,也就是根据 a2 引用了一个 B 类型对象实例所以从 B 类型开始查找 show 方法,这时候立刻找到了 A 的的第二个 show 方法有一个 B 类型上的重写,子类B类扩展(修改)了A类的特性(通过override)。 在写好和测试好一些针对父类/父接口的代码工程的前提下,子类既继承了父类特性又重写了父类的个别特性,这就是面向对象技术。这需要分析一些应用领域的对象类型/接口之间的继承关系,并且实际操作程序来在针对父类型/接口的代码中使用子类型的对象实例,慢慢实际体会。
zhuowp 2018-01-16
  • 打赏
  • 举报
回复
引用 9 楼 wkqwe 的回复:
[quote=引用 8 楼 zhuo_wp 的回复:]
A a = (A)b是强制类型转换,其实这里是不必要的,因为子类可以隐式转换成父类 A a = b也是没问题的。这里强转一下只是为了演示用。
引用
父类的引用指向子类的对象
要明白这句话你最好搞清楚什么是什么是变量、什么是变量的引用。
简单的说b是一个类的实例,一个对象,它初始化后放在托管堆上,而A a = b这句代码是把b这个对象的引用(可以简单而不严谨的认为是对象在内存上的地址)赋值给了变量a。此时代表父类型A的变量a就指向了子类型B的一个实例b。

我可能比较笨大神....我是这样理解的:a对象去调用show()方法,传的值是A类型的a.不是应该直接去A类中去执行show(A obj)方法吗? 结果输出A and A 看不到为啥会输出C and A
还有这个b对象的创建我也看不懂 ,不是这样吗B b = new B(); 为啥要写成B b = new C();?
[/quote]


这个前面也说过,调用a.Show()确实是去调用类A 的方法,但是因为Show本身是一个虚方法,所以他会调用距离基类最远的子类中的那个复写的实现
这上面截图的这个例子中,用C 实例化了a,由于C继承B,B继承A,而B类中复写了A的虚方法,所以调用的时候会直接调用B类中的方法的实现。
  • 打赏
  • 举报
回复
比如对于3,编译器要从A类型上(因为你的a1变量声明为A类型)找最合适的Show方法重载,于是找到了A方法的第一个函数。 重载是很古老的技术,从c、c++中就很普遍,是40年的技术。而面向对象设计中的继承、多态、重写是比较新的,是20年的技术。实际开发中,如果面向对象方式设计开发,写出这类 Show 方法,那么就会很自然地避免用Show函数多次重载来混淆代码设计。即使要重载,那么也会让Show方法在参数个数上明显不同,而且参数类型也有天壤之别(例如string跟int这种一眼就能看出不兼容的类型),绝不会用什么“D obj、A obj”这种存在着兼容性的混淆做法。 而且在A类型中就出现了后续多层继承之后的子类型D作为参数,这明显就是故意来捣乱的一个问题,是故意用来考试的。实际设计中一个人如果画出整个系统类型图,他一眼就能看出这种依赖关系又多了乱、多么垃圾,他就不会采纳这种设计。但是初学者不知道如何凭经验来把握设计原则,只知道盲目解题,自然就容易被那些老司机出的考试题给忽悠得晕头转向。
  • 打赏
  • 举报
回复
引用 25 楼 wanghui0380 的回复:
所以一个比较简单的,你能看的懂的描述是 皇帝说要盖间屋 要金丝楠木,到宰相那里换成黄花梨,到县官那里换成了普通硬木 盖房 x=new 谁呢? 无论他override多少级,主要看你右边new了谁,是让谁做。 皇帝做就是金丝楠木,县官做就是普通硬木
我差不多懂了,但是我分析a2.show(d)应该输出B类中的show(B obj)即B and B 盖房 x=new 谁呢? == A a2 = new B(); 无论他override多少级,主要看你右边new了谁,是让谁做。 皇帝做就是金丝楠木,县官做就是普通硬木 结果执行了A中的show(D obj)方法。。这是怎么回事??
  • 打赏
  • 举报
回复
在实际设计开发中,通常不会这种又多层继承(重写)、又在许多层次都重载的做法。这个例子可以说把问题搞得很复杂很繁琐,主要是摆着用来考试的,实际中都会很自觉地避免这种设计。但是这种设计用来考试,还是不错的!
  • 打赏
  • 举报
回复
引用 楼主 wkqwe 的回复:
求分析每一个输出的原因。(注释的是输出的结果——一个都看不懂) 这里父类的引用指向子类的对象怎么理解?
确定函数重载到底是什么,要看函数签名,也就是说不仅仅看函数名、还要看所有函数参数的类型和排列顺序而定。 比如说对于5而言,编译器确定要调用A类型上第二个Show方法,因为A类型的第一个方法跟参数c是不兼容的。然后运行时,a2引用了一个B类型对象实例,那么就会调用那个被重写了(注意这里是重写而不是重载)的Show方法,所以打印 B and A。 再比如说8,编译器从B类型上找方法定义,发现第一个Show方法最兼容,因为第二个Show方法的参数定义为A类型而第一个方法定义为B类型更接近于调用参数c类型,所以编译认定第一个Show方法最兼容。
正怒月神 2018-01-16
  • 打赏
  • 举报
回复
父类的引用指向子类的对象 你就当 派生类赋值给基类。
  • 打赏
  • 举报
回复
引用 12 楼 wkqwe 的回复:
[quote=引用 11 楼 xuzuning 的回复:] 为啥要写成B b = new C(); B b 是说 b 是 B 的引用,而 new C() 是 C的实例(B 还是 C 的父类 这就是你说的 父类的引用指向子类的对象
我理解的是这样的,结果编译不通过啊?是我理解的问题吗?[/quote] 是你理解问题。编译器使用强类型方式帮你检查类型声明兼容性。变量 a 声明为A类型,虽然它可能引用兼容于A的任何对象(这里是B类型对象实例),但是编译器不认识 a.name。多态是指不同类型的子类对象实例可能会执行不同的重写了的方法,也就是子类可以重写父类的定义,对于B类型就是 Show 方法重写,而跟 name 无关。
wanghui0380 2018-01-16
  • 打赏
  • 举报
回复
至于你的例子里那些同名的函数,那是故意混淆的。那是方法重载,对于方法重载看方法签名(方法签名看参数的个数,参数的类型),使用不是他同名就不知道怎么调用,同名函数的方法签名也会不同,如果有相同的方法签名,vs是不会让你编译过去的
wanghui0380 2018-01-16
  • 打赏
  • 举报
回复
所以一个比较简单的,你能看的懂的描述是 皇帝说要盖间屋 要金丝楠木,到宰相那里换成黄花梨,到县官那里换成了普通硬木 盖房 x=new 谁呢? 无论他override多少级,主要看你右边new了谁,是让谁做。 皇帝做就是金丝楠木,县官做就是普通硬木
加载更多回复(18)

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧