基礎類型

全部的類型都從 System.Object 派生

一、類型System.Object

運行時要求每個類型都是從System.Object派生,若是沒有顯示的寫明繼承關係,最後都會默認的從System.Object來派生。System.Object提供了四個公用方法和兩個收保護方法:編程

公共方法 方法說明
Equals 比較兩個對象的值是否相等,相等返回true
GetHashCode 返回對象的哈希碼,若是想讓對象在字典中作鍵使用,應當重寫此方法提供一個分佈均勻的哈希碼,此方法本應該設計在接口中
ToString 默認返回類型的完整名稱:this.GetType().F ullName()。VS調試器會自動調用改函數來顯示對象的字符串表示。理論上此函數應該察覺到與調用線程的CutureInfo並採起相關行動
GetType 返回從Type派生的一個類型實例,是非虛方法。目的是爲了防止類型重寫改方法,隱瞞其類型,從而破壞其安全性
MemberwishClone 建立一個新實例,而且設置新的實例對象字段和當前對象相同的值,而後返回其引用
Finalize 垃圾回收以前執行,若是須要處理某些資源能夠重寫此方法

二、new 操做符須要乾的事情

- 計算類型以及其基類(直到system.object)的實例字段須要的字節數,以及堆上額外成員須要的字節數。額外成員包括 類型對象指針 和同步索引塊 - 從託管堆中分配本身數從而分配對象內存 - 初始化 類型對象指針 和同步索引塊 成員的值 - 調用構造函數。每個構造函數都負責初始化該類型定義的實例字段 - 返回一個引用安全

類型轉換

三、類型安全

    CLR 最重要的一個特性就是類型安全。覺得不論是什麼變量,調用一下GetType就能夠知道他的具體類型,此方法是非虛方法,不能夠進行假裝和重寫。而同時C#也規定,不要求任何特殊語法,就可將對象轉換成對應的任何基類,由於面相基類的轉換默認爲是類型安全的。類型假裝是不少安全問題的根源,也會破壞程序的健壯性和穩定性。數據結構

四、is 和 as 運算符

    is 運算符專門用來檢查對象是否能夠兼容的轉換爲另外的一個類型的對象,永遠不會拋出錯誤,只是會返回true或者false.ide

  if(o is Employee)
    {
        Employee e = (Employee)o;
    }

 

    上面的代碼中,作了兩次類型檢查,加強了安全性,但無疑對性能形成必定的浪費。普通的強制類型轉換過程:CLR首先必須判斷變量(o)引用的對象的實際類型,而後CLR 類型便利繼承的層次結構,用每個基礎類型去核對制定的類型(Employee)。函數

    as 運算符就是爲了簡化上面的經常使用編程模式而設計的,as運算符只是檢查一次對象,若是對象位null就返回位null,而後就進行轉換,若是轉換不了就返回位null,而永遠不會拋出錯誤。以下代碼,if中間只是判斷一個是否位null,相比速度會快不少性能

 

Employee e = o as Employee;
if(e != null)
{
    
}

 

命名空間和程序集

    命名空間只是對相關類型進行邏輯分組。對於編譯器而言,命名空間的做用就是爲了類型名稱架上點分隔符讓名稱變得更加長,也更加具備惟一性。this

    命名空間只是針對編譯器,而CLR對命名空間是無概念的,在訪問類型的時候,CLR須要知道類型的完整名稱以及該類型定義在哪個程序集裏面,這樣「運行時」才能正確的找到和加載正確的程序級,而且對其進行操做spa

    命名空間,和程序級之間無直接關係,一個命名空間的代碼能夠出如今多個程序級中,一個程序級也能夠有多個命名空間。能夠參考,System以及Linq相關程序級和命名空間線程

    using運算符,只是爲了方便開發人員,少寫一些類的全稱部分,一樣只是針對編譯器有用,CLR並不認識這個語法。另一個做用就是容許給類型和命名空間建立別名,以下:設計

using WintellectWidget = Wintellect.Widget 
public sealed class Program 
{ 
    public static void Main() 
    {
     WintellectWidget = new WintellectWidget(); 
    }
} 

 

    若是仍是有在不一樣程序級中,同名的類型,此種狀況能夠經過外部程序級來解決(extern alias)

運行時的相互關係

    假定有以下兩個類型的定義

  internal class Employee {
      public int GetYearsEmployed(){} 
      public virtual string GetProgressReport(){} 
    public static Employee Lookup(string name){}
  } 
  internal sealed class Manager:Employee 
  { 
      public overide String GetProgressReport(){}
  }

 

    以下圖,程序以及執行過一段時間,如今即將調用M3方法。JIT將M3的IL代碼編譯成CPU的指令時,會注意到全部的類型,確保以及加載了具體對應的程序集。利用程序集的元數據,CLR提取與之相關的信息,建立數據結構來表示類型自己

    如圖中,Employee和Manager類型對象都包含兩個成員:類型對象指針和同步索引塊。而在定義類型的時候,能夠在類型內部定義靜態數據字段,俄日這些靜態數據字段提供支援的字節在類型對象自身中分配,每個類型對象都包含一個方法表,在方法表中國,類型定義的每個方法都有對應的記錄項

 

    而後,M3執行代碼構造一個Manager對象,形成在託管堆上建立Manager類型的一個實例對象。此對象也有類型對象指針和同步索引塊,同時還包含必要的字節來容納Manager類型定義的全部實例數據字段,以及容納有Manager的任何基類定義的全部實例字段。

    任什麼時候候在堆上建立對象時,CLR會自動初始化內部的"類型對象指針」成員來引用和對象對應的類型對象(也就是Manager類型對象),在執行構造函數以前還會先初始化同步索引塊,而且將對象的實例字段設置爲null或者0.而後經過new操做符返回地址

    M3的下一行代碼是調用Employee的靜態方法 Lookup 獲得一個Manager對象。調用靜態方法的時候,CLR會首先定位到靜態方法的類型對應的類型對象,而後,JIT編譯器在類型對象方法表中查找與被調用方法的對應的記錄項,對方法進行JIT編譯,而後在調用編譯好的代碼。

    M3接下來調用非虛實例方法,GetYearsEmployed。調用虛方法時,JIT編譯器會找到發出調用的那個變量(e)的類型(Employee)對應的類型對象。若是找不到,就回溯到 object 類,之因此能回溯,是由於每個對象都有一個字段引用了它的基類類型。從方法記錄表中找到被調用方法的記錄項後,進行JIT編譯,編譯完成後,在進行方法的調用。

    接下來調用的是Empoloyee的虛實例方法GetProgressReport。調用時,JIT要先在方法中生成一些額外的代碼,每次都會執行這些代碼。這些代碼,首先檢查發出調用的變量,而後跟隨地址來到發出調用的對象,變量e引用的是 manager 對象,而後代碼檢查對象的內部的「類型對象指針」成員,改爲員指向了實際的類型。而後在對類型的對象方法表中查找被調用的方法的記錄向,對方法進行JIT編譯,而且執行。

     Employee和Manager類型對象都會包含一個「類型對象指針」成員,因爲類型對象自己也是對象,CLR在建立類型對象時,必須初始化這些成員。CLR開始在進程中運行時,會當即爲MSCorLib.dll中定義的System.Type類型對象建立一個特殊的類型對象。Employee和Manager類型對象都是改類型的「實例」,所以,他們的類型對象指針成員都會初始化成對System.Type類型對象的引用。固然,System.Type類型對象自己也是對象,內部也有「類型對象指針」成員,這個指針就指向了他自己,由於這個System.Type類型對象自己是一個類型對象的「實例」。而System.Object的GetType方法返回存儲在指定對象的「類型對象指針」成員中的地址,也就是說,GetType方法返回執行對象的類型對象指針,這樣就能夠判斷任何對象的真實類型。

相關文章
相關標籤/搜索