深入理解.net运行时方法表

在.net运行时,每一个类型在创建第一个实例,或者静态成员被第一次访问,或者被反射创建时,就会创建一个与该类型关联的方法表:

基本结构大概如下:

+--------------------------+
|      Method Table        |
+--------------------------+
|   Virtual Method #1 ptr  |
|   Virtual Method #2 ptr  |
|   ...                    |
|   Non-virtual Method ptr |
+--------------------------+
|   Type Information       |
|   (Size, Base Type ptr,  |
|    ... other info)       |
+--------------------------+

其中重点分为两部分,方法表与描述类型的元数据。

方法表中的每一项包含了方法的标识信息与指针ptr,指针指向了方法体在内存中的实际地址。

方法表中的项又分为两类:虚方法与实方法,两者的区别体现在方法表的继承上。

举例子,我创建了如下两个类,他们存在继承关系:

public class BaseClass
{public virtual void MethodA(){Console.WriteLine("BaseClass.MethodA");}public virtual void MethodB(){Console.WriteLine("BaseClass.MethodB");}
}public class DerivedClass : BaseClass
{public override void MethodB(){Console.WriteLine("DerivedClass.MethodB");}public virtual void MethodC(){Console.WriteLine("DerivedClass.MethodC");}
}

两个类型的方法表如下:

基类BaseClass的方法表:

+------------------------+
|      Method Table      |
+------------------------+
|   Virtual Method #1    | 函数指针指向  -->  BaseClass.MethodA
|   Virtual Method #2    | 函数指针指向  -->  BaseClass.MethodB
+------------------------+

 子类DerivedClass的方法表:

子类继承了基类的方法表项MethodA与MethodB
+------------------------+
|      Method Table      |
+------------------------+
|   Virtual Method #1    | 未重写,所以依旧指向  -->  BaseClass.MethodA
|   Virtual Method #2    | 重写了,所以指向  -->  DerivedClass.MethodB
|   Virtual Method #3    |   -->  DerivedClass.MethodC
+------------------------+

基于这种机制,就实现了多态,在创建实例时,实例的“对象头”中包含了指向自身类型所对应的方法表的指针,基于此,找到方法表,再根据方法标识找到对应的方法表项,从而就自然而然找到了指向方法体的指针。

实例的“对象头”大概结构如下:

+----------------------+
|      对象头           |
+----------------------+
|   类型指针            |   -->  指向方法表的指针
|   同步锁信息          |   -->  用于同步的信息
|   标记信息            |   -->  垃圾回收标记信息
|   ...其他元信息...    |
+----------------------+
|      对象数据         |   -->  实际存储对象数据的部分
+----------------------+

当将“实例”作为参数传递时,所传递的是“实例的引用”,说人话就是传递指向上述结构中“对象头”部分起始位置的指针,找到了“对象头”,运行时自然也就能够知道该对象的类型信息了,从而实现了高级的语言特性,如多态性、垃圾回收等。调用虚方法、判断对象的实际类型等操作都依赖于这种引用机制,使得对象在运行时能够灵活地适应不同的上下文。

抽象类

抽象类的方法表(Method Table)与普通类的方法表类似,不过抽象类的方法表中可能包含抽象方法。抽象方法是一种只有方法签名而没有具体实现的方法,需要在派生类中进行实现。

让我们通过一个简单的例子来理解抽象类的方法表。假设有如下的抽象类:

public abstract class MyBaseClass
{public abstract void AbstractMethod();public void ConcreteMethod(){Console.WriteLine("Concrete method in MyBaseClass");}
}

对应的抽象类方法表可能类似于以下结构:

+------------------------+
|      Method Table      |
+------------------------+
|   Virtual Method #1    |   -->  MyBaseClass.AbstractMethod
|   Virtual Method #2    |   -->  MyBaseClass.ConcreteMethod
+------------------------+

在这个示例中:

  • AbstractMethod 是一个抽象方法,它在抽象类中只有方法签名而没有具体实现。
  • ConcreteMethod 是一个普通的方法,它有具体的实现。

抽象方法在方法表中会被表示为虚方法(Virtual Method),因为它们需要在派生类中进行具体的实现。派生类中实现的具体方法会在其自己的方法表中添加相应的条目。

当一个派生类继承了抽象类并实现了抽象方法时,它的方法表可能会变成类似这样:

+------------------------+
|      Method Table      |
+------------------------+
|   Virtual Method #1    |   -->  DerivedClass.AbstractMethod (实现抽象方法)
|   Virtual Method #2    |   -->  MyBaseClass.ConcreteMethod
+------------------------+

这样,抽象类的方法表中包含了抽象方法和具体方法的信息,而派生类的方法表中则包含了实现的具体方法的信息。这种继承关系和方法表的组织方式使得多态性在抽象类和派生类中得以实现。

接口Interface

接口(Interface)在.NET中也有与之相关的方法表(Method Table)的概念,不过与类的方法表有一些区别。接口中的方法表用于存储接口中定义的方法的签名,而不包含具体的实现。

让我们通过一个简单的例子来理解接口的方法表。假设有如下的接口:

public interface IMyInterface
{void InterfaceMethod();
}

对应的接口方法表可能类似于以下结构:

+------------------------+
|      Method Table      |
+------------------------+
|   Virtual Method #1    |   -->  IMyInterface.InterfaceMethod
+------------------------+

在这个示例中:

  • InterfaceMethod 是接口中定义的方法,它只有方法签名而没有具体实现。

当一个类实现了接口时,它会实现接口中定义的方法,并且在该类的方法表中会包含接口方法的实现。例如,如果有如下的类:

public class MyClass : IMyInterface
{public void InterfaceMethod(){Console.WriteLine("InterfaceMethod implementation in MyClass");}
}

MyClass 的方法表可能会变成类似这样:

+------------------------+
|      Method Table      |
+------------------------+
|   Virtual Method #1    |   -->  MyClass.InterfaceMethod (实现接口方法)
+------------------------+

这样,接口的方法表记录了接口方法的签名,而实现了接口的类的方法表包含了实际的方法实现。这种方式支持了类对多个接口的实现,实现了接口隐式地引入了多态性,允许通过接口引用调用具体实现的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/241425.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

持续集成交付CICD:Jira 远程触发 Jenkins 实现更新 GitLab 分支

目录 一、实验 1.环境 2.GitLab 查看项目 3.Jira新建模块 4. Jira 通过Webhook 触发Jenkins流水线 3.Jira 远程触发 Jenkins 实现更新 GitLab 分支 二、问题 1.Jira 配置网络钩子失败 2. Jira 远程触发Jenkins 报错 一、实验 1.环境 (1)主机 …

HarmonyOS构建第一个JS应用(FA模型)

构建第一个JS应用(FA模型) 创建JS工程 若首次打开DevEco Studio,请点击Create Project创建工程。如果已经打开了一个工程,请在菜单栏选择File > New > Create Project来创建一个新工程。 选择Application应用开发&#xf…

Docker知识总结

Docker 学习目标: 掌握Docker基础知识,能够理解Docker镜像与容器的概念 完成Docker安装与启动 掌握Docker镜像与容器相关命令 掌握Tomcat Nginx 等软件的常用应用的安装 掌握docker迁移与备份相关命令 能够运用Dockerfile编写创建容器的脚本 能够…

go语言学习计划。

第1周:Go语言概述与环境搭建 内容:了解Go语言的历史、特点和应用场景。安装Go环境,配置工作区。实践:编写第一个Go程序,了解Go的编译运行流程。 第2周:基本语法与数据类型 内容:学习基本数据…

排序算法——基数排序

将需要排序的各个数当做元素,集合组成数组,对数组中的元素进行排序,再开辟一个临时数组的空间将数组中已有的元素数值当做临时数组的下标储存在临时数组中,然后用区别初始化值的方法区别出临时数组中待排数组的元素,以…

全方位掌握卷积神经网络:理解原理 优化实践应用

计算机视觉CV的发展 检测任务 分类与检索 超分辨率重构 医学任务 无人驾驶 整体网络架构 卷积层和激活函数(ReLU)的组合是网络的核心组成部分 激活函数(ReLU) 引入非线性,增强网络的表达能力。 卷积层 负责特征提取 池化层…

[MySQL] 二进制文件

文章目录 日志文件binlog是什么简介产生方式文件格式statementrowmixed 怎么办设置文件存储格式缓冲区大小的调整方式缓冲区写入文件的时机 sync_binlog 日志文件 官网 https://dev.mysql.com/doc/refman/8.0/en/server-logs.html中文版 https://mysql.net.cn/doc/refman/8.0/e…

OpenCV | 霍夫变换:以车道线检测为例

霍夫变换 霍夫变换只能灰度图,彩色图会报错 lines cv2.HoughLinesP(edge_img,1,np.pi/180,15,minLineLength40,maxLineGap20) 参数1:要检测的图片矩阵参数2:距离r的精度,值越大,考虑越多的线参数3:距离…

快速安装方式安装开源OpenSIPS和CP控制界面

OpenSIPS是目前世界上主流的两个SIP软交换引擎(其中另外一个是kamailio)或者SIP信令服务器(个人认为是比较正确的称谓)。关于Opensips的基础和一些参数配置和安装方式笔者在很久以前的历史文档中有非常多的介绍。最近,很多用户使用OpenSIPS软…

《PySpark大数据分析实战》-18.什么是数据分析

📋 博主简介 💖 作者简介:大家好,我是wux_labs。😜 热衷于各种主流技术,热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员(PCTA)、TiDB数据库专家(PCTP…

EPROM 作为存储器的 8 位单片机

一、基本概述 TX-P01I83 是以 EPROM 作为存储器的 8 位单片机,专为多 IO 产品的应用而设计,例如遥控器、风扇/灯光控制或是 玩具周边等等。采用 CMOS 制程并同时提供客户低成本、高性能等显着优势。TX-P01I83 核心建立在 RISC 精简指 令集架构可以很容易…

【零基础入门Docker】什么是Dockerfile Syntax

✍面向读者:所有人 ✍所属专栏:零基础入门Docker专栏https://blog.csdn.net/arthas777/category_12455882.html 目录 编写Dockerfile和Format的语法 2. MAINTAINER 3. RUN 4. ADD 6. ENTRYPOINT 7. CMD 8. EXPOSE 9. VOLUME 11. USER 12. ARG …

Git指令集合

文章目录 1. 撤销暂存区内容2.修改commit comment3. 合并分支3.1 git rebase3.2 git merge 4.撤销前3次commit指令5. cherry pick git指令有很多,常用的也就是那么几个,今天回顾一下几个准常用,很重要的几个指令。 1. 撤销暂存区内容 当我把…

【iOS】UICollectionView

文章目录 前言一、实现简单九宫格布局二、UICollectionView中的常用方法和属性1.UICollectionViewFlowLayout相关属性2.UICollectionView相关属性 三、协议和代理方法:四、九宫格式的布局进行升级五、实现瀑布流布局实现思路实现原理代码调用顺序实现步骤实现效果 总…

centos 安装 Miniconda

在 CentOS 上安装 Miniconda 的步骤通常包括下载 Miniconda 安装脚本、运行脚本以及配置环境。以下是详细步骤: 1. 下载 Miniconda 安装脚本 首先,您需要从 Miniconda 的官方网站下载适用于 Linux 的安装脚本。您可以使用 wget 命令在 CentOS 终端中直…

在ajax中使用callback

今天遇到个场景是在点击按钮时,调用请求,在请求完成后,获取到后台返回的某个值之后再去执行下一步操作,也就是要同步进行。其实用setTimeout也是可以实现的,但考虑到效果最好,决定使用callback。 getData …

Pytohn data mode plt

文章目录 文件的读写创建.csv类型的文件,并读取文件创建.xlsx文件 使用Python做图生成数据集切片取值操作修改张量中指定位置的数据 知识点torch.arange(x)torch.tensor(2)Atorch.randn(36).reshape(6,6)shapenumel()reshape(x,y,z)torch.zeros(3,3,4)torch.ones(2,…

论文推荐:大型语言模型能自我解释吗?

这篇论文的研究主要贡献是对LLM生成解释的优缺点进行了调查。详细介绍了两种方法,一种是做出预测,然后解释它,另一种是产生解释,然后用它来做出预测。 最近的研究发现,即使LLM是在特定数据上训练的,也不能认…

【WPF.NET开发】WPF中的数据绑定

本文内容 什么是数据绑定数据绑定基本概念数据绑定的示例创建绑定数据转换绑定到集合数据模板化数据验证调试机制 Windows Presentation Foundation (WPF) 中的数据绑定为应用呈现数据并与数据交互提供了一种简单而一致的方法。 元素能够以 .NET 对象和 XML 的形式绑定到不同…

【微服务】:微服务最佳实践

关键需求 最大限度地提高团队的自主性:创建一个团队可以完成更多工作而不必与其他团队协调的环境。 优化开发速度:硬件便宜,人不是。使团队能够轻松快捷地构建强大的服务。 关注自动化:人们犯错误。更多的系统操作也意味着更多的…