C# 类继承中的私有字段都去了哪里?
最近在看 C++ 类继承中的字段内存布局,我就很好奇 C# 中的继承链那些 private 字段都哪里去了? 在内存中是如何布局的,毕竟在子类中是无法访问的。
一:举例说明
为了方便讲述,先上一个例子:
internal class Program
{
static void Main(string[] args)
{
Chinese chinese = new Chinese();
int num = chinese.b; //b 字段无法访问,编译报错
Console.WriteLine(num);
}
}
public class Person
{
public int a = 10;
private int b = 11;
}
public class Chinese : Person
{
public int c = 12;
}
根据 C# 的类继承原则,上面的 chinese.b
写法肯定是无法被编译的,因为它属于父类的 私有字段,既然无法被访问,那这个 private b
到底去了哪里呢? 要想找到答案,只能先从 chinese
实例处的汇编代码看起,看看有没有什么意外收获。
二:查看 chinese 处汇编代码
在 new chinese()
处下一个断点,查看 Visual Stduio 2022
的反汇编窗口。
接下来我稍微解读下:
1. 根据 MT 类型 实例化 chinese
07FD6176 mov ecx,87205C4h
07FD617B call CORINFO_HELP_NEWSFAST (06E30C0h)
这里的 87205C4h
就是 Chinese 类型的 MT,然后通过 CLR 下的 CORINFO_HELP_NEWSFAST
处的方法进行实例化。
2. 使用 chinese 的构造函数进行类初始化
07FD6180 mov dword ptr [ebp-40h],eax
07FD6183 mov ecx,dword ptr [ebp-40h]
07FD6186 call CLRStub[MethodDescPrestub]@7e34871e07fd5d20 (07FD5D20h)
07FD618B mov eax,dword ptr [ebp-40h]
这里的 eax 是 CORINFO_HELP_NEWSFAST
初始化方法的返回值,可以在 ecx,dword ptr [ebp-40h]
处下一个断点,观察它的内存布局。
从布局图看,此时的 chinese 只是一个清零的默认状态,此时的 a,b,c
三个字段还没有被赋值,那什么时候被赋值呢? 这就是构造函数要做的事情了,也就是上面的 CLRStub[MethodDescPrestub]@7e34871e07fd5d20 (07FD5D20h)
指令,接下来在 07FD618B
处下一个断点,再次观察 0x02C9F528
处的内存地址,也就是 ebp-40
的位置,接下来我们继续执行,截图如下:
从图中可以看到,当构造函数执行完之后,有三处内存地址(变红)被赋值了,依次是 a,b,c
,这时候是不是让人眼前一亮。
3. 洞察真相
原来那个 b=11
并没有丢,而是被 chinese
类给完全继承下来的,而且布局规则是 父类
字段在前, 子类
字段在后的一种方式,有点意思,接下来的问题是如何把它提取出来?
三:如何提取 b 字段
如果是 C 语言,我们用 *(pointer+2)
就可以轻松提取,那用托管的 C# 如何去实现呢? 可以用复杂的 Marshal
包装类,应该也可以变相的使用 Span
去搞定,这里我就不麻烦了,直接用非安全代码下的 指针
去摆平,在 a
字段偏移 +4 的位置上提取, 参考代码如下:
static void Main(string[] args)
{
unsafe
{
Chinese chinese = new Chinese();
fixed (int* ch = &chinese.a)
{
int b = *(ch + 1);
Console.WriteLine($"b={b}");
}
}
}
}
哈哈,是不是挺有意思。
最新文章
- JAVA学习笔记(33-53)
- SPOJ220 Relevant Phrases of Annihilation(后缀数组)
- myeclipse 第一个web project
- angularjs 利用filter进行表单查询及分页查询
- each函数循环数据表示列举,列举循环的时候添加dom的方法
- 跟我学android-	创建运行环境(二)
- pyqt menu子级方向例子学习
- API例子:用Python驱动Firefox采集网页数据
- 软交所--微软将对IE浏览器进行关键性安全更新
- 细说在兄弟连搞上PHP的那些事儿
- ubuntu 14.04 64位 安装Opencv3.1.0 (包含opencv_contrib模块)
- Python列表的深浅复制
- vue.js 监听属性的学习/ 千米、米的转换 /时、分、秒 的转换
- Python-csv模块读写csv文件
- Codeforces Beta Round #65 (Div. 2) C. Round Table Knights
- Django的视图函数和路由系统中一些没有用过的小点
- SP1716 GSS3 - Can you answer these queries III
- [转]关于适配iphone5,Invalid Launch Image的退信
- MMO技能系统的同步机制分析
- python 进程间通信(下)
热门文章
- win10 Celery异步任务报错: Task handler raised error: ValueError('not enough values to unpack (expected 3, got 0)
- PAT B1002写出这个数
- 通过CSS给图像设置圆角边框
- uni-app中遇到的跳转问题
- PyQt5 基本语法(四)
- formdata收集数据
- Svelte3聊天室|svelte+svelteKit仿微信聊天实例|svelte.js开发App
- 『现学现忘』Git基础 — 7、设置Git Bash终端默认路径
- Android四大组件——Activity——Activity之间通信上
- JavaScript一行代码获取公网IP及城市信息