什么是多态:书上定义是 重载 和向后运行机制
重载:都比较清楚, 相同名字的方法,但是参数不同,(数量与类型信息不同)
不考虑返回值,原因是没必要设计的这么复杂,不然在编译期就通过
ps:现有的 var 推断方法 其实也是可以的,但是还是没必要
向后运行机制:
public class AObjct : Object
{
public override String ToString()
{
return "AObjct";
}
}
Object o = new AObject(); o.ToString()
其中 ToString() 和 父级的名称 入参都相同, o.ToString() 调用的是 哪个方法?
我相信在这里大家都比较清楚 是 AObjct 里面的方法?但是为什么如此?
1. 方法是什么?
方法对象,其名称 可以简单的认为 是名+参数的包装形式。所以任何来自于相同的Type 类型对应的方法对象GetHashCode都是同一个对象。
Object a = new Object();
Object b = new Object();
Console.WriteLine(b.GetType().GetMethod("ToString") == c.GetType().GetMethod("ToString"));//true
所以方法是一个MethodInfo对象,并且所有来自于同一个类型的方法对象都是同一个。
也就是说 我在调用方法的时候 是直接把 MethodInfo 的 IL代码加载到 线程上,然后传递 对象(this),以及定义的参数。
其中如果标识了 static 方法 则不需要传递对象this值,即可直接使用,区别是 一个验证 抛出异常,一个不验证,但是实际都是相同的
2.方法怎么验证传递的对象
public class BaseClass { public void Test() { } }
public class SubClass : BaseClass { }
Console.WriteLine(typeof(BaseClass).GetMethod("Test") == typeof(SubClass).GetMethod("Test"));
//答案是false
原因是 子类在生成MethodInfo的时候 会单独生成全部的方法,也就是俗称的代码爆炸
所以比较 粗暴,直接验证是否是 BaseClass.GetType() 以及 验证 SubClass.GetType().并不是很多人以为的 先去找子的,然后再去找父类型的(如果这个样子 那么 这个等式就是相等的)
但是注意,验证对象的方式是 直接比较对象,但是生成的都是对象名 是 BaseClass.Test 。
可以理解成,会生成 2次 名叫BaseClass.Test 的方法,只不过一个是 BaseClass 类型的,一个是 SubClass类型的
3.方法如何调用
关键字 Call Callvirt
这两个是IL代码关键词,但是在讲这两个词的时候,就得先知道 另外几个关键词
virtual / new / static / override 如果什么不加,那么 默认是 new
这里面 virtual overrde 是向后运行机制,也就是在调用方法的时候 找其 对象 所对应的类型实际的方法
new static 就是直接使用当前的方法
案例的话 我一言带过,大家不懂得 就得多看书了
那代码是如何分辨出 哪些是 需要向后运行 哪些是 直接调用?
实际是 看 方法名 有没有 virtual 或 override,
在说下,override 是语法糖,实际 对应的是 virtual,也是由于这种,所以孙类 也可以 重写 标识 override的方法
当标识为 virtual (override 也是virtual),那么在编译的时候 是放到一个方法数组,然后调用的时候 先 调用GetType()获取真实的类型,然后从方法数组里面匹配真实方法类型,如果 当前类没 override 此方法,还得 调用父类继续循环匹配最近的父类对应的 virtual方法。
由于这个机制导致 向后运行机制是最慢的调用方法,先去堆上调用GetType,再从堆上的数组里匹配方法,匹配不到,还得获取父类方法,再去匹配。
那会询问,
如何确定 方法组,
答案:所有的 virtual 及 Override的方法共用最顶级方法名
如 toString,即使你重写了,这个方法名 也是 Object.ToString,而不是子类的 Sub.ToString(),除非你加了 New
那如果是 New的方法,如何调用?
编译的时候 直接 调用方法,如 BaseClass.Test,会直接调用 Test(),而不是从数组堆里寻找。即使你调用 SubClass.Test,其实也是直接调用 Test(),只不过在判断入参的时候 是判断是否是子类。
所以总结 多态实质:
1. 单个类里面方法都是独立的个体,对于计算机来说 名称都是不同的
2. 不同的类可以有重复的名称,通过 Call Callvirtual 来区分,其中如果遇到 Virtual 标识的,从数组里面一个个 通过GetType()来匹配,所以速度最慢,而 如果是New 或者是 static 那么是单对单调用,区别是 Call 不需要验证类型,即不需要重新调用堆上的GetType获取实质的类型,而 Callvirtual 需要getType来获取实质类型,
所以 Call最快,
不加 virtual 及 New 的次之,只需要验证当前类型的GetType
加了virtual不仅调用当前类型的GetType 还得调用方法的GetType
工具:大家 可以结合ILDasm工具查看 IL代码
1. 找到 C:\Program Files (x86)\Microsoft SDKs\Windows,随便打开某个,建议都选中最新的
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\ildasm.exe
2.VS 上添加工具
工具=》外部工具=》添加
名称 ILDasm
地址 就是 你具体的 exe 地址
参数 填 $(TargetPath)
3.应用,添加
4.
4. 注意,查看之前得 编译到 exe里面,快捷键是 F6
点击 ILDasm 确定,双击任何红色的,我们直接看 Program.Main
然后大家结合工具,多想想调用方式。其实和自己写代码是相同的,只不过这里用的是 IL代码而已
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。