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

news/2024/12/29 16:49:13/

在.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.ppmy.cn/news/1280594.html

相关文章

模型性能评估-混淆矩阵简介

混淆矩阵 Positive - 正例Negative (N) - 负例 结果: 预测为正类别 预测为负类别 真实为正类别 True Positive (TP) False Negative (FN) 真实为负类别 False Positive (FP) True Negative (TN)TP - 预测 P, 实际 P, 模型预测正确FP - 预测 P, 实际 N, 模…

HarmonyOS - 鸿蒙开发入门

文章目录 HarmonyOS核心资源特性:全场景终端HarmonyOS 版本 HarmonyOS 和 OpenHarmony教程资源开发环境开发工具 - DevEco开发语言 - ArkTS核心框架 - ArkUI 考证 HarmonyOS 开发交流秋秋群:23458659,V : ez-code,期待交流和合作 …

光耦继电器

光耦继电器(光电继电器) AQW282SX 282SZ 280SX 280SZ 284SX 284SZ 212S 212SX 21 2SZ 文章目录 光耦继电器(光电继电器)前言一、光耦继电器是什么二、光耦继电器的类型三、光电耦合器的应用总结前言 光耦继电器在工业控制、通讯、医疗设备、家电及汽车电子等领域得到广泛应…

k8s学习 — (运维)第九章 Helm 包管理器

k8s学习 — (运维)第九章 Helm 包管理器 学习资料1 什么是 Helm?2 Helm 架构2.1 重要概念2.2 组件2.2.1 Helm 客户端2.2.2 Helm 库 3 安装 Helm4 Helm 的常用命令5 chart 详解5.1 目录结构5.2 Redis chart 实践5.2.1 修改 helm 源5.2.2 搜索 …

【Android 13】使用Android Studio调试系统应用之Settings移植(二):构建settings app项目目录

文章目录 一、篇头二、系列文章2.1 Android 13 系列文章2.2 Android 9 系列文章2.3 Android 11 系列文章三、准备工作3.1 创建目录3.2 初始化 git 仓库四、提取settings原始代码4.1 提取目标4.2 源码路径4.2.1 settings app4.2.2 SettingsLib4.3 存放位置

各大高校科研工具链培训PPT汇总

各大高校科研工具链培训PPT汇总 RSS 北邮图书馆:通过RSS订阅高效获取信息、追踪研究前沿山东大学图书馆:如何追踪学科研究前沿苏大图书馆:个人知识管理软件的使用中科院图书馆:利用RSS与最新资讯同步 文献管理工具 中南大学图…

JAVA WEB用POI导出EXECL多个Sheet

前端方法:调用exportInfoPid这个方法并传入要查询的id即可,也可以用其他参数看个人需求 function exportInfoPid(id){window.location.href 服务地址"/exportMdsRoutePid/"id; } 后端控制层代码 Controller Scope("prototype") R…

IPC之九:使用UNIX Domain Socket进行进程间通信的实例

socket 编程是一种用于网络通信的编程方式,在 socket 的协议族中除了常用的 AF_INET、AF_RAW、AF_NETLINK等以外,还有一个专门用于 IPC 的协议族 AF_UNIX,IPC 是 Linux 编程中一个重要的概念,常用的 IPC 方式有管道、消息队列、共…