C# 类继承中的私有字段都去了哪里?
时间:2022-08-17 22:00:01
最近在看 C 我对类继承中的字段内存布局很好奇 C# 那些继承链 private 字段在哪里? 如何布局内存,毕竟在子类中是无法访问的。
一、举例说明
先举个例子,方便讲述:
internalclassProgram { staticvoidMain(string[]args) { Chinesechinese=newChinese(); intnum=chinese.b;//b不能访问字段,编译报错 Console.WriteLine(num); } } publicclassPerson { publicinta=10; privateintb=11; } publicclassChinese:Person { publicintc=12; }
根据 C# 类继承原则,上述类继承原则chinese.b
写作不能编译,因为它属于父类 既然私有字段不能被访问,那么这个private b
到底去了哪里?要想找到答案,只能先从chinese
看实例中的汇编代码,看看有没有意想不到的收获。
二:查看 chinese 处汇编代码
在new chinese()
查看下一个断点Visual Stduio 2022
反汇编窗口。
接下来我稍微解释一下:
1. 根据 MT 类型 实例化 chinese
07FD6176movecx,87205C4h 07FD617BcallCORINFO_HELP_NEWSFAST(06E30C0h)
这里的87205C4h
就是 Chinese 类型的 MT,然后通过 CLR 下的CORINFO_HELP_NEWSFAST
实例化处方法。
2. 使用 chinese 构造函数初始化
07FD6180movdwordptr[ebp-40h],eax 07FD6183movecx,dwordptr[ebp-40h] 07FD6186callCLRStub[MethodDescPrestub]@7e34871e07fd5d20(07FD5D20h) 07FD618Bmoveax,dwordptr[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 提取位置, 参考代码如下:
staticvoidMain(string[]args) { unsafe { Chinesechinese=newChinese(); fixed(int*ch=&chinese.a) { intb=*(ch 1); &nbp; Console.WriteLine($"b={b}");
}
}
}
}
哈哈,是不是挺有意思。