您真的了解类型转换吗?请止步,求解!

  前阵子,一名同事问及类型转换的问题,我也仅仅说出目前自己的了解。但回头想想,其中的确大有学问,以前只看到了表面,其内在的表现如何,苦苦翻书,Google几番之后,依然无所收获,故大胆写下,求园中各位大牛不吝解答。

  类型转换的疑惑

  首先,我们知道类型转换也就那点事(表面的说),总归而言,C#下有几种转换,装箱,拆箱,向上类型转换,向下类型转换,平行类型转换几种。这几种的区别目前也不细说了,感兴趣的童鞋可移步C# 装箱和拆箱[整理],向上类型转换,向下类型转换,平行类型(.Net本质论79页-运行时的类型)。

  依照以往的知识,现在我们假设有一个Person类,再有一个Employee类,Employee继承Person,声明一个Employee的实例,并将其赋给一个Person的实例,由于类型是引用类型,则实际上它们都是指向同一个对象实例。代码如下:

Employee kinsen = new Employee("Kinsen", "Chan");

Person kong = kinsen;

  这一点应该是毫无疑问的。问题是,已知在构造一个实例的时候,实际上是在堆栈上开辟一块空间,这块空间包含三块,分别是同步块,类型句柄,以及实例具体信息。我们就是通过类型句柄来获得该实例的具体对象。但此时Person类实例kong指向的是kinsen实例的地址,那么该类型句柄的信息也应该是Employee而非Person的。但偏偏我们却能正确的获取到Person的方法,也能正确的执行,看一下代码:

public class Program

{

static void Main(string[] args)

{

Employee kinsen = new Employee("Kinsen", "Chan");

kinsen.SayHello();

Person kong = kinsen;

kong.SayHello();

}

public class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

public Person(string firstname, string lastName)

{

this.FirstName = firstname;

this.LastName = lastName;

}

public void SayHello()

{

Console.WriteLine("Hello,Word");

}

public override string ToString()

{

return FirstName + " " + LastName;

}

}

public class Employee : Person

{

public Employee(string firstname, string lastname) : base(firstname, lastname) { }

new public void SayHello()

{

Console.WriteLine("Hello,Word!My Name is " + base.FirstName);

}

}

}

  执行结果如下:

  在这里我并没有采用虚方法,否则结果都是第一个了。可见,即使kong引用的是kinsen,但实际上它执行的还是Person类的方法。那么,到底是从哪里得知kong是Person类对象的呢?再见一个实验。

Employee kinsen = new Employee("Kinsen", "Chan");

kinsen.SayHello();

Person kong = kinsen;

kong.SayHello();

object obj = kong;

Console.WriteLine(kinsen.GetType());

Console.WriteLine(kong.GetType());

Console.WriteLine(obj.GetType());

  除了Employee和Person类实例,我们还将kong赋给了一个object,然后输出实例的类型,结果如下:

  可以看到,三个实例的实际类型都是Employee,但是Person类实例的确是执行了Person类的SayHello方法啊。这到底是为什么?

  到处寻找答案,在《.Net本质论》79页中找到这么一段话:

当从一个对象引用的类型转换到另一个对象引用的类型时,必须考虑两个类型之间的关系。如果初始化引用的类型被认定与新引用的类型兼容,那么,CLR所要做的转换只是一个简单的IA-32 mov指令。这通常出现于这样的赋值情形中;当一个派生类型的引用到一个直接或间接基类的引用,或则到一个一直兼容的接口引用。

  从这段话中,了解到为什么结果三个实例的类型都是Employee,但我想解决的问题还没解决,为何指向Employee实例引用的Person类实例还能准确的找到它的类型呢?目前我已知的信息如下:

  实例地址,也就是线程栈上的地址,它只包含一个指向堆栈引用的指针。

  堆栈内存块,也就是线程栈上保存那个指针指向的地址,它包含三部分,同步快,类型句柄,实例信息。其中类型句柄起到标识该实例所属类型,所拥有方法表等信息,但现状是三个实例指向的都是同一个内存地址,也就是它们是一模一样的。那它们到底是如何识别的?

  内存中的表现形式

  我再借助SOS来探查具体的信息,稍微改动了下代码,以便在SOS中更好查看。

static void Main(string[] args)

{

new Program().Run();

}

Employee kinsen;

object obj;

Person kong;

public void Run()

{

kinsen = new Employee("Kinsen", "Chan");

kinsen.SayHello();

kong = kinsen;

kong.SayHello();

obj = kong;

Console.WriteLine(kinsen.GetType());

Console.WriteLine(kong.GetType());

Console.WriteLine(obj.GetType());

}

  通过!ClrStack命令得到当前对象的地址:

000000000023e7e0 000007ff00190163 DebugTest.ClassConvert.Run()

PARAMETERS:

this = 0x00000000023c5ad0

0:000> !dumpobj 0x00000000023c5ad0

Name: DebugTest.ClassConvert

MethodTable: 000007ff00033b68

EEClass: 000007ff00182250

Size: 40(0x28) bytes

(E:\Projects\ClassConvert.exe)

Fields:

MT Field Offset Type VT Attr Value Name

000007ff00033d68 4000001 8 DebugTest.Employee 0 instance 00000000023c5b48 kinsen

000007fef43773f8 4000002 10 System.Object 0 instance 00000000023c5b48 obj

000007ff00033ca0 4000003 18 DebugTest.Person 0 instance 00000000023c5b48 kong

  这里能看到DebugTest.ClassConvert类有三个实例,分别是kinsen,obj和kong,它们的value都相同(00000000023c5b48),这里的确与程序中看到的一模一样,但是注意,它们的MT,也就是方法表却不一样了。再分别把他们解析一下。

//Name=kinsen

0:000> !dumpvc 000007ff00033d68 00000000023c5b48

Name: DebugTest.Employee

MethodTable 000007ff00033d68

EEClass: 000007ff00183498

Size: 32(0x20) bytes

(E:\Projects\ClassConvert.exe)

Fields:

MT Field Offset Type VT Attr Value Name

000007fef4377b08 4000004 0 System.String 0 instance 000007ff00033d68 <FirstName>k__BackingField

000007fef4377b08 4000005 8 System.String 0 instance 00000000023c5af8 <LastName>k__BackingField

//Name=obj

0:000> !dumpvc 000007fef43773f8 00000000023c5b48

Name: System.Object

MethodTable 000007fef43773f8

EEClass: 000007fef3f42200

Size: 24(0x18) bytes

(C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)

Fields:

//Name=kong

0:000> !dumpvc 000007ff00033ca0 00000000023c5b48

Name: DebugTest.Person

MethodTable 000007ff00033ca0

EEClass: 000007ff001833f0

Size: 32(0x20) bytes

(E:\Projects\ClassConvert.exe)

Fields:

MT Field Offset Type VT Attr Value Name

000007fef4377b08 4000004 0 System.String 0 instance 000007ff00033d68 <FirstName>k__BackingField

000007fef4377b08 4000005 8 System.String 0 instance 00000000023c5af8 <LastName>k__BackingField

  或许到这里,的确能解释为什么即使指向的对象是同一个,但却能在转换成其他类型之后做该类型的操作,但是其中还是如一个黑匣子,我对此依然不明不白。

  令人向往的方法表,方法槽表

  此外,还有一张图,

  这张图出自微软,对于一些概念,我还是比较模糊,例如方法表,方法槽表,SOS中的MT应该是方法表呢?还是方法槽表?从图上看来,方法表的分布比较散,看起来好像没什么规则,这样又如何确定方法槽表,方法表与方法槽表之间的关系又是如何呢?很希望大家能踊跃回答,如果有详细的资料就更好了。

  渴望音讯

  关于这个类型转换,类型句柄,方法表的问题纠结折腾了我许久,实在没办法了。才大胆发出来,恳求各位前辈,大大能帮小弟解惑。

  更多

  可能也有童鞋也和我有一样的疑问,以下是一些我查找的知识点来源:

  揭示同步块索引(上):从lock开始

  揭示同步块索引(中):如何获得对象的HashCode

  揭示同步块索引(下):总结

  关于CLR内存管理一些深层次的讨论[上篇]

  深入探索.NET框架内部了解CLR如何创建运行时对象

时间: 2024-09-20 21:35:24

您真的了解类型转换吗?请止步,求解!的相关文章

c#-C++C# 数据类型转换,请各位大神帮忙,很急

问题描述 C++C# 数据类型转换,请各位大神帮忙,很急 typedef void(* heartbeatcallback)(BYTE hosttype,BYTE netid,int Account,BYTE state,BYTE Arm,BYTE byPass,BYTE Notready,BYTE ACError,BYTE BatteryError,BYTE TellineError,BYTE GSMError,BYTE GPRSError,BYTE ipinfo[32]); 这个用C# 怎么

请高手求解啊

问题描述 给定一个字符串,要求写一个函数来求出这个字符串里面第一个重复的字符,例如字符串"abcda",第一个重复的字符是a.abcdba,第一个重复的字符是b,希望能用java给个完整答案,谢谢 解决方案 解决方案二:该回复于2010-04-25 08:42:34被版主删除解决方案三:publicstaticvoidmain(String[]args){Stringstr="abcdb";System.out.println("第一个重复的是:"

我被论坛版主封了IP,我该怎么办?请大家帮帮我,谢谢,小弟刚进论坛,没什么分,日后一定补偿,谢谢大家的信任~!

问题描述 是这样的,有一次我在论坛灌水,结果给版主封了IP,使我在论坛无法发言了,我非常后悔!可惜版主不给我机会,我也不懂封IP是个什么意思,当时被版主封IP的时候,我用的是家里的台式电脑,电信宽带ADSL的,你说我该怎么办?为了能够上那个论坛,我现在刚买了一台笔记本,还没联过网的,我打算用笔记本3G无限网络去上这个论坛,我会重新注册一个ID,包括密码也重新设置一个,邮箱也换,总之什么都和以前的ID不一样,那么请问高手们,我用笔记本电脑再去登陆这个论坛,版主还会知道是我吗?你们说3G无限网络和A

Visual Studio 2008发现疑问求解

问题描述 我之前使用VisualStudio2005StandardEdition(简体中文版),现在我有一台电脑装了VisualStudioTeamSystem2008(试用版)然后输入CDKEY升级到了正式版,疑问就是在2005建立任何工程时,"解决方案配置"有"Debug"和"Release","解决方案平台"有".NET"和"AnyCPU"和"MixedPlatform

急!急!!急!!!求解如何在WebService中使用Session或Cookie---WebService安全策略

问题描述 现在我们开发一个项目,业务逻辑层全部用WebService,客户端有Flex和Asp.Net,Flex和Asp.Net的所有业务逻辑数据库的访问全部调用WebService中的方法(好象Flex也不能直接对数据库进行访问,所以也只能用WebService),现在就出现问题了,就是WebService中的权限问题了,比如:我在WebService中有一个Login的方法,还有一个ChangePwd修改用户密码的方法,可之,如果我要调用ChangePwd方法前我一定要判断用户是否Login

社会化电子商务用户研究:主动消费和被动消费

文章描述:主动消费和被动消费. 以下是我最近半年来所思所想,由于工作原因所说的话只能停留在"道"的层面上,没有具体的做法和实际案例,内容较"虚".如果您想看到具体的实在的操作层面内容,请止步. 1.名词 所谓"主动消费",春天到了我需要买双不是那么厚的鞋子:所谓"被动消费",虽然春天到了但我有几双不是那么厚的鞋子穿,不那么需要再买一双.因为别人说某个鞋子不错.看到某个地方在打折.看到某个鞋子很不错.- 我又买了几双不是那么厚的

自由谈:为什么使用 Linux

Liberty means responsibility. That is why most men dread it. -  George Bernard Shaw Unless a man has talents to make something of himself, freedom is an irksome burden. -  Eric Hoffer 起这么吓人的一个题目,其实只是想写一下 Linux.但我保证决不做标题党. 请注意,这是一篇充满正能量富有哲理的小清新鸡汤文,操作系

分级制度未建立腾讯推《御龙在天》需审慎

腾讯前期给玩家留下千丝悬念的X-GAME,在千呼万唤之中,终于解开了面纱:一款名为<御龙在天>的网游高调面世,且先不管玩家对这款游戏是否山寨的评论,就凭此款游戏官网公开的极具震撼的"十八禁"概念,就使<御龙在天>备受关注. 通过<御龙在天>官方公布的游戏CG和特色资料可看出,血腥暴力的渲染.深卧床中较为暴露的女性等等,再加之写实的美术风格,使得此游戏具备十八禁的内涵.但是,十八禁这个概念对于传统观念根深蒂固的我们来说,再加之目前分级制度尚未建立,就使

思创SEO:期待有一个更好的绿色互联网平台

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 前几天在微博上,看到郭吉军截图自己的QQ群尽然超过30个居多,这个也确实让我感觉汗颜,不为别的,以前看看我个人QQ加入的群列表,顶多的时候也就10几个,这样的情况下都感觉QQ蹦来蹦去的,不知道他那边怎么受得了,除非直接屏蔽掉,太多的时间花费在了这些上,QQ发展到今天,目前使用QQ的人几乎跟手机一样的作为了人们交流联系的工具,暂且不说上次QQ和