<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      您真的了解類型轉換嗎?請止步,求解!

      2011-08-24 23:57  空逸云  閱讀(4675)  評論(100)    收藏  舉報

      前陣子,一名同事問及類型轉換的問題,我也僅僅說出目前自己的了解。但回頭想想,其中的確大有學問,以前只看到了表面,其內在的表現如何,苦苦翻書,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);
                  }
              }
          }
      

      執行結果如下:

      image

      在這里我并沒有采用虛方法,否則結果都是第一個了。可見,即使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,然后輸出實例的類型,結果如下:

      image

      可以看到,三個實例的實際類型都是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如何創建運行時對象

      主站蜘蛛池模板: 欧美午夜小视频| 国内精品视频一区二区三区八戒| 色偷偷女人的天堂亚洲网| 97人人添人澡人人爽超碰| 国产超碰人人做人人爰| 国产仑乱无码内谢| 999国产精品一区二区| 伊人蕉久影院| 凉城县| 国产区精品视频自产自拍| 国产在线一区二区在线视频| 办公室强奷漂亮少妇视频| 精品国产免费一区二区三区香蕉| 国产av熟女一区二区三区| 国产成人av电影在线观看第一页| 免费看国产精品3a黄的视频| 亚洲深深色噜噜狠狠网站| 视频一区视频二区视频三| 人妻少妇不满足中文字幕| 最近中文字幕国产精选| 国产极品美女高潮抽搐免费网站 | 我要看亚洲黄色太黄一级黄| 在线看国产精品三级在线| 全免费A级毛片免费看无码| 国产成人一区二区三区免费| 精品国产一区二区色老头| 色偷偷av一区二区三区| 国产精品午夜福利精品| 午夜综合网| 精品夜恋影院亚洲欧洲| 国产一区在线播放av| 国产日产免费高清欧美一区| 国产精品电影久久久久电影网| 国产欧美精品一区二区三区四区| 亚洲精品久久国产高清小说| 亚洲成人av在线系列| 女人裸体性做爰视频| 国产亚洲精品成人av久| 成人做受120秒试看试看视频| 妇女性内射冈站hdwww000| 国产亚洲精品成人av久|