前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >多态实质

多态实质

原创
作者头像
洪移潮
发布2024-12-10 20:48:16
发布2024-12-10 20:48:16
600
举报
文章被收录于专栏:C#从中级到高级C#从中级到高级

什么是多态:书上定义是 重载 和向后运行机制

重载:都比较清楚, 相同名字的方法,但是参数不同,(数量与类型信息不同)

不考虑返回值,原因是没必要设计的这么复杂,不然在编译期就通过

ps:现有的 var 推断方法 其实也是可以的,但是还是没必要

向后运行机制:

代码语言:txt
复制
    public class AObjct : Object
    {
        public override String ToString()
        {
            return "AObjct";
        }
    }
    Object o = new AObject(); o.ToString()

其中 ToString() 和 父级的名称 入参都相同, o.ToString() 调用的是 哪个方法?

我相信在这里大家都比较清楚 是 AObjct 里面的方法?但是为什么如此?

一:方法是什么?

1. 方法是什么?

方法对象,其名称 可以简单的认为 是名+参数的包装形式。所以任何来自于相同的Type 类型对应的方法对象GetHashCode都是同一个对象。

代码语言:txt
复制
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.方法怎么验证传递的对象

代码语言:txt
复制
    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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一:方法是什么?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档