程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> CLR via C#深解筆記二,

CLR via C#深解筆記二,

編輯:C#入門知識

CLR via C#深解筆記二,


類型基礎 所有類型都從System.Object派生   CLR要求所有對象都用new 操作符來創建。 Employee e = new Employee("Constructor Parameters");   以下是 new 操作符所做的事情: #1, 計算類型及所有基類型(一直到System.Object, 雖然它沒有定義自己的實例字段)中定義的所有實例字段需要的字節數。 堆上的每個對象還需要一些額外(overhead 開銷成員)的成員 -- 即“類型對象指針”(type object pointer)和“同步塊索引”(sync block index)。這些成員由CLR用於管理對象。這些額外成員的字節數會計入對象大小。 #2, 它從托管堆中分配指定類型要求的字節數,從而分配對象的內存,分配的所有字節都設為0。 #3, 它初始化對象的“類型對象指針”和“同步塊索引”成員。 #4, 調用類型的實例構造器,向其傳入在對new 的調用中指定的任何實參。大多數編譯器都在構造器中自動生成代碼來調用一個基類構造器。每個類型的構造器在調用時,都要負責初始化由這個類型定義的實例字段。最總調用的是System.Object的構造器,該構造器只是簡單地返回,不會做其他任何事情。 new執行了所有操作之後,會返回指向新建對象一個引用(或指針)。在前面的示例代碼中,這個引用會保存到變量e中,後者具有Employee類型。   類的“新實例”和“實例成員”:兩種不同的“實例”。一種是類的實例,也就是具體的對象。另一種是類中定義的實例字段。所謂“實例字段”,就是指非靜態字段,有時也稱為“實例成員”。簡單地說,實例成員是屬於類的對象的, 而靜態成員是屬於類的。   類型安全 CLR總是知道一個對象(某個類型的實例)是什麼類型。所有表達式都解析成某個類型的實例,在編譯器生成的代碼中,只會執行對這個類型來說有效的操作。與非類型安全的語言相比,類型安全的語言的優勢在於:程序員會犯的許多錯誤能在編譯時檢測到,確保代碼在你的嘗試執行它之前是正確的。除此之外,編譯時語言通常能生成更小、更快的代碼,因為他們能在編譯時進行更多的假設,並在生成的IL和元數據中落實那些假設。   類型轉換 CLR最重要的特性之一就是類型安全性。在運行時,CLR總是知道一個對象是什麼類型。調用GetType() 方法,總是知道一個對象確切的類型是什麼。 開發中,開發人員會經常將一個對象從一種類型轉換為其他各種類型。CLR允許將一個對象轉換為它的(實際)類型或者它的任何基類型。 #1: 向基類型的轉換被認為是一種安全的隱式轉換。 #2: 將對象轉換為它的某個派生類型時,C#要求只能進行顯式轉換,因為這樣的轉換有可能在運行時失敗。在運行時,CLR檢查類型操作,確定總是轉換為對象的實際類型或者它的任何基類型。   這就是類型安全的設計。如果CLR允許這樣的轉型,就無類型安全性可言了,將出現難以預料的結果 -- 其中包括應用程序崩潰,以及安全漏洞的出現(因為一種類型能輕松地偽裝成另一種類型)。 類型偽裝是許多安全漏洞的根源,它還會破壞應用程序的穩定性和健壯性。類型安全是CLR一個重要的目標。   is 操作符,檢查一個對象是否兼容於指定的類型,並返回一個Boolean值;true或false。注意 is 操作符永遠不會拋出異常。如果對象引用是null,is操作符總是返回false。   Object o = new Object(); if (o is Employee) {      Employee e = (Employee)o; }   這段代碼中,CLR實際上會檢查兩次對象的類型。 CLR 的類型檢查增強了安全性,但無疑也會對性能造成一定影響。因為CLR首先必須判斷變量引用的對象的實際類型。然後,CLR必須遍歷繼承層次結構,用每個基類型去核對指定的類型(如Employee)。 上面這個事一個相當常用的編程模式,所以C#專門提供了as操作符,目的就是簡化這種代碼的寫法,同時提升性能。   Employee e = o as Employee; if(e != null) {      //..... }   as 操作符的工作方式與強制類型轉換一樣,只是它永遠不會拋出一個異常 -- 相反,如果對象不能轉型,結果就是null。所以,正確做法也就是檢查最終生成的引用是否為null。應該不要直接使用最終生成的引用,否則可能會拋出一個System.NullReferenceException 異常。   注意:C#允許在一個類型中定義轉換操作符方法。只有在使用一個轉型表達式時,才會調用這些方法;使用C#的as或者is操作符時,永遠不會調用他們。   命名空間 (namespace) 用於對相關的類型進行邏輯性分組,開發人員可以使用命名空間方便地定位一個類型。例如,System.Text命名空間定義了一組執行字符串處理的類型。 using 指令指示編譯器為每一個類型附加不同的前綴,直到找到一個匹配項。using的使用,不僅極大地減少打字量,還有助於增強代碼的可讀性。 using指令還支持另一種形式,允許為一個類型或者命名空間創建別名。如果只想使用一個命名空間中的少數幾個類型,不希望它的所有類型都跑出來“污染”全局命名空間,別名就顯得十分方便。   using System; using jack = CSI.Widget;   重要提示:CLR並不知道命名空間的任何事情。訪問一個類型時,CLR需要直到類型的完整名稱(可能是一個相當長的、包含句點符號的名稱)以及該類型的定義具體在哪一個程序集中。這樣一來,“運行時”才能加載正確的程序集,找到目標類型,並對其進行操作。   編譯器會掃描引用的所有程序集,在其中查找類型的定義。一旦找到正確的程序集,程序集信息和類型信息就會嵌入最終生成的托管模塊的元數據中。為了獲取程序集信息,必須將定義了“引用的類型”的程序集傳給編譯器。 默認情況下,C#編譯器會自動在MSCorLib.dll 程序集中查找 “引用的類型”,即使你沒有顯式告訴它這樣做。MSCorLib.dll程序集中包含了所有核心Framework類庫(FCL)類型的定義,比如Object, Int32, String等。   命名空間和程序集(實現了一個類型的文件)不一定是相關的。特別是,同一個命名空間中的各個類型可能是在不同的程序集中實現的。在一個程序集中,也可能包含不同命名空間中的類型。   運行時的相互關系   類型、對象、線程棧和托管堆在運行時的相互關系。調用靜態方法、實例方法和虛方法的區別。     已經加載了CLR的一個Microsoft Windows 進程。這個進程中,可能存在多個線程。一個線程的創建時,會分配到一個1MB大小的棧。這個棧的空間用於向方法傳遞實參,並用於方法內部定義的局部變量。棧是從高位內存地址向低位內存地址構建的。     棧幀(stack frame)代表的是當前線程的調用棧中的一個方法調用。在執行線程的過程中進行的每個方法的調用都會在調用棧中創建並壓入一個stack frame                    至此我們討論了源代碼、IL和JIT編譯的代碼之間的關系,還討論了線程棧、實參、局部變量以及這些實參和變量如何引用托管堆上的對象。我知道了,對象中包含一個指針,它指向對象的類型對象(類型對象中包含靜態字段和方發表)。 還討論了JIT編譯器如何決定靜態方法、非虛實例方法以及虛實例方法的調用方式。這一切的理解,可以幫助深刻地認識CLR的工作方式。   注意,Employee和Manager類型對象都包含“類型對象指針”成員。這是由於類型對象本質上也是對象。CLR創建類型對象時,必須初始化這些成員。初始化成什麼呢?CLR開始在一個進程中運行時,會立即為MSCorLib.dll中定義的System.Type類型創建一個特殊的類型對象。Employee和Manager類型對象都是該類型的“實例”。因此,它們的類型對象指針成員會初始化成對System.Type類型對象的引用,如下面所示。     -----------------------------------------------------------  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved