由於所有類型最終都是從System.Object派生的,所以可以保證每個類型的每個對象都有一組最基本的方法。
System.Object提供了如下所示的公共實例方法。
Equals(Object) 確定指定的對象是否等於當前對象。如果兩個對象具有相同值就返回ture. GetHashCode 返回對象的值得一個哈希碼。如果某個類型的對象要在哈希表集合中作為key使用,該類型應該重寫這個方法。方法應該為不同的對象提供一個良好的分布。 ToString 該方法默認返回類型的完整名稱(this.GetType().FullName)。 GetType 返回從Type派生的一個對象的實例,指出調用GetType的那個對象是什麼類型。返回的Type類型可以與反射類配合使用,從而獲取與對象的類型相關的元數據信息。
MemberwiseClone 這個非虛方法能創建類型的一個新實例,並將對象的實例字段設為與this對象的實例字段完全一致。返回的是對新實例的一個引用 Finalize 在垃圾回收器判斷對象應該被作為垃圾收集之後,在對象的內存被實際回收之前,會調用這個虛方法。需要在回收之前執行一些清理工作的類型應該重寫這個方法。
Employee e = Employee();
以下是new操作符所做的事情:
1)它計算類型及其所有基類型(一直到System.Object)中定義的所有實例需要的字節數。堆上的每個對象都需要一些額外的開銷成員——"類型對象指針(type object pointer)"和"同步塊索引"(sync block index)。這些成員由CLR用於管理對象。這些額外成員的字節數會計入對象大小。
2)它從托管堆中分配指定類型要求的字節數,從而分配對象的內存,分配的所有字節都設為零(0)。
3)它初始化對象的"類型對象指針"和"同步塊索引"成員。
4)調用類型的實例構造器,向其傳入對new的調用中指定的任何實參(本例中是"
new 執行了所有的操作後,會返回執行新建對象的一個引用。在本例中,這個引用會保存到變量e中,具有Employee類型。
注意:上面提到過"類型對象指針",類型對象不是類型的對象/實例,這兩者是有區別的。
----------------------------------------------------------------------------------
CLR最重要特性之一就是類型的安全性。在運行時,CLR始終知道一個對象的類型,可以調用GetType方法,得到對象的類型。
CLR允許將一個對象轉換為它的實際類型或者它的任何基類型。
C#不要求使用特殊語法即可將一個對象轉換為它的任何及類型,因為向基類型的轉換被認為是一種安全的隱式轉換。但是,將對象轉換為它的某個派生類時,C#要求開發人員只能進行顯示轉換,因為這樣的轉換在運行時可能失敗。
public static void Main() { // 不需要轉型 Object o = new Employee(); // 需要進行強制類型轉換 Employee e = (Employee) o; }
在C#語言中進行類型轉換的另一種方式是使用is操作符。is操作符檢查一個對象是否兼容指定的類型,並返回一個Boolean值(true和false)。注意,is操作符是不會返回異常信息的。
is操作符通常這樣使用:
if ( o is Employe ){ Employee e = (Employee) o; }
在這段代碼中,CLR實際是會檢查兩次對象的類型。is操作符首先核實o是否兼容Employee類型。如果是,在if內部,CLR還會再次核實o是否引用一個Employee。CLR的類型檢查增強的安全性,但無疑也會對性能造成一定影響。
C#專門提供了 as 操作符,目的就是簡化這種代碼的寫法,同時提升性能。
as操作符通常這樣使用:
Employee e = o as Employee; if ( e != null ){ //在if中使用e }as操作符的工作方式與強制類型轉換一樣,只是它是不會拋出異常的,如果不能轉化,結果就是null。所以,正確的做法就是檢查最終生成的引用是否為null。如果企圖直接使用轉換後的引用,就會拋出異常。 ---------------------------------------------------------------------------------- 命名空間(namespace)用於對相關的類型進行邏輯分組,開發人員使用命名空間來方便的定位一個類型。
命名空間和程序集不一定是相關的,也就是說它們之間沒有必然聯系。
----------------------------------------------------------------------------------
現在將解釋類型、對象、線程棧和托管堆在運行時的相互聯系。此外,還將解釋調用靜態方法、實例方法和虛方法的區別。
我們先從線程棧開始。
1. 圖4-2展示了已加載了CLR的一個Windows進程。在這個進程中,可能存在多個線程。一個線程創建時,會分配到一個1MB大小的棧。這個棧的空間用於向方法傳遞實參,並用於方法內部定義的局部變量。圖4-2展示了一個線程的棧內存(右側)。棧是從高地址向低地址構建的。在圖中,線程已執行了一些代碼,現在,假定線程開始執行的代碼要調用M1方法了。
2. 在一個最基本的方法中,會有一些"序幕"代碼,負責在方法開始時做它工作之前對其進行初始化。另外,還包括了"尾聲"代碼,負責在方法完成工作之後對其進行清理,然後才返回至調用者。M1方法開始執行時,它的"序幕"代碼就會在線程棧上分配局部變量name的內存,如圖4-3所示。
3. 然後,M1調用M2的方法,將局部變量name作為一個實參來傳遞。這造成name局部變量中的地址被壓入棧(參見圖4-4)。在M2方法內部,將使用名為s的參數變量來標識棧位置(有的CPU架構會通過寄存器來傳遞實參,以提高性能)。另外,調用一個方法時,還會將一個"返回地址"壓入棧中。被調用的方法在結束後,應該返回到這個位置(同樣參見圖4-4)。
internal class Employee { public int32 GetYearsEmployed() { ... } public virtual String GenProgressReport() { ... } public static Employee Lookup(String name) { ... } } internal sealed class Manager : Employee { public override String GenProgressReport() { ... } }
2. 我們的Windows進程已啟動,CLR已加載到其中,托管堆已初始化,而且已創建一個線程(連同它的1MB的棧空間)。該線程已執行了一些代碼,現在馬上就要調用M3的方法。圖4-6展示了目前的狀況。M3方法包含的代碼演示了CLR是如何工作的。
GetType