所有類型都繼承自System.Object System.Object的基本方法 | 名稱 | 訪問級別|說明 | | | | | |Equals()|public|如果兩個對象有相同的值就返回true| |GetHashCode()|public|返回對象的值的哈希碼 |ToString()|pu ...
所有類型都繼承自System.Object
System.Object的基本方法
名稱 | 訪問級別 | 說明 |
---|---|---|
Equals() | public | 如果兩個對象有相同的值就返回true |
GetHashCode() | public | 返回對象的值的哈希碼 |
ToString() | public | 預設返回類型的完整名稱(this.GetType().GetFullName) |
GetType() | public | 指出調用GetType()方法的對象的類型 |
MemberwiseClone() | protected | 可以創建類型的一個新的實例,並將新對象的實例欄位與this設置一致,返回的是新實例的引用 |
new操作符
CLR要求所有對象都是通過new來創建的,如下
Book book = new Book("語文");
new操作符創建對象的過程:
1.計算類型及其所有基類類型(直到Object
)中定義的所有實例欄位需要的位元組數;堆上的對象成員還需要計算“類型對象指針”和“同步塊索引”的字計數,這些成員由CLR管理。
2.從托管堆中分配類型要求的位元組數,分配的所有位元組都設置為0。
3.初始化對象的“類型對象指針”和“同步快索引”。
4.對用類型的實例構造器,向其傳入在對new的調用中指定的任何參數(上面代碼中傳入的“語文”);編譯器會在構造函數中生成代碼對基類構造器進行調用,每個類型的構造器在調用時,都負責初始化由這個類型定義的實例欄位;由於Object
類型沒有實例欄位,該構造器只是簡單的返回,並沒有任何操作。
執行完new操作後,會返回一個新建的對象引用(或指針),如上面的代碼將這個引用保存到了變數book
中。
類型轉換
CLR的一個重要特性就是類型安全,在運行時,總是知道一個對象是什麼類型。
CLR允許將一個對象轉換為它的派生類型和基類型;向基類型轉換時是安全的隱士轉換,向派生類轉換時有可能在運行時會失敗,所以只能進行顯示轉換。
//Object是Teller的基類,直接隱式轉換
object o = new Teller();
//需要強制轉換
Teller teller = (Teller)o;
使用is和as操作符來轉型
is
檢查對象是否相容於制定類型,返回一個布爾值;is操作符永遠不會拋出異常,即使對象引用的是null。
if (obj is Teller)
{
Teller teller = (Teller)obj;
}
上面的代碼是is
的常用操作方式,CLR會對對象進行兩次類型檢查,雖然這樣增強了類型的安全性檢查,但無疑浪費了性能;為了避免這個問題,可以通過as
的方式進行轉型。
Teller teller = obj as Teller;
if (teller != null)
{
}
CLR會檢測obj
是否相容於Teller
,如果是,as
返回一個非null
的引用,如果不相容,as
操作符將返回null
。
命名空間和程式集
命名空間用於對相關類型進行邏輯性的分組,如System.Text
定義了一組字元串處理的類型
System.Text.StringBuilder builder = new System.Text.StringBuilder();
顯然,這樣寫代碼非常繁瑣;C#編譯器通過使用using
減少了輸入量。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TypeBasis
{
class Program
{
static void Main(string[] args)
{
StringBuilder s = new StringBuilder();
}
}
}
對於編譯器來說,命名空間的作用就是使類型更具有唯一性。當兩個類型名稱發生衝突,為了消除歧義,必須顯示的告訴編譯器使用的是哪一個類型。使用using
還可以創建類型或者命名空間的別名。
using System;
using ST = System.Text;
namespace TypeBasis
{
class Program
{
static void Main(string[] args)
{
ST.StringBuilder builder = new ST.StringBuilder();
}
}
}
CLR不知道命名空間的任何類型,訪問一個類型時,CLR需要知道類型的完整名稱。
當兩個類型的命名空間及類型名稱都一樣的時候,可以通過外部別名消除歧義;為了避免發生這種問題,應該常使用公司名稱(而不是公司名稱首字母縮寫或者其他簡寫)來作為自己的頂級命名空間名稱。
namespace
指令的作用是告訴編譯器為源代碼中出現的每個類型名稱附加命名空間名稱首碼,減少程式員的輸入量。
命名空間和程式集之間不一定是相關的,同一個命名空間也有可能在不同的程式集中。
運行時的相互聯繫
通過下麵的代碼來瞭解運行時間的相互關係
class Program
{
static void Main(string[] args)
{
//當程式運行時,並即將調用到上面的`Main`中的`DoSomething()`方法時,首先JIT(即時編譯器)將`DoSomething()`的IL(中間代碼)轉換成CPU指令,
//並確保方法中的定義了的類型的程式集都已經載入,利用程式集的元數據,CLR創建一些結構數據來表示類型本身;在每個對象中,最後都會包含一個方法表,類型中定義的每個方法都有一個對應的記錄
//當`DoSomething()`方法開始執行時,會自動為方法中的局部變數初始化值(`null`或者0),如果未顯示為局部不變數初始化,調用的時候將引發“使用了未賦值的局部變數”異常
DoSomething();
}
static void DoSomething()
{
Employee e;
int year;
//創建Manager類型的一個實例,並分配實例所需要的記憶體(實例欄位及所有基類的實例欄位所需的位元組數),並將實例欄位設置為null或者0
//然後再調用構造函數(構造函數的本質是修改實例欄位的一個方法)
//new操作符將Manager對象的記憶體地址返回,並保存在變數e中
e = new Manager();
//調用靜態方法時,CLR會定位定義靜態方法的類型對應的類型對象,然後JIT在類型對象中查找與被調用的方法相關的記錄,對方法JIT編譯,再調用JIT編譯的代碼
//e不再引用上面的Manager對象,它將被GC回收,GC回收後將釋放它所占用的記憶體,e將保存由Employee.Lookup("Joe")返回來的一個新的地址
e = Employee.Lookup("Joe");
//調用Employee類型實例的GetYearEmployed方法,如果Employee中沒有定義GetYearEmployed方法,JIT編譯器會沿著Employee的基類查找,知道Object
year = e.GetYearEmployed();
//如果Employee.Lookup("Joe")返回的是一個Manager引用,將調用Manager類型中的GenProgressReport重寫方法,如果返回的是Employee類型引用,調用的就是Employee中的虛方法
e.GenProgressReport();
}
}
public class Employee
{
public int GetYearEmployed()
{
//TODO: do something...
return default(int);
}
public virtual string GenProgressReport()
{
//TODO: do something...
return default(string);
}
//靜態成員所需的位元組包含在對象本身
public static Employee Lookup(string name)
{
//TODO: do something...
return default(Employee);
}
}
public class Manager : Employee
{
public override string GenProgressReport()
{
//TODO: do something...
return default(string);
}
}
CLR創建類型對象時,必須初始化“類型對象指針”成員;CLR開始在一個進程中運行時,會立即為MSCorLib.dll
定義的System.Type
類型創建一個特殊的類型對象。Manager
和Employee
都是該對象的“實例”,因此,它們的對象指針成員會初始化為對System.Type
的類型對象的引用;當然System.Type
的“類型對象指針”成員指向的是它本身。
System.Object
的GetType()
方法返回的是存儲在制定對象的“類型對象指針”成員中的地址,即指向對象的類型對象的一個指針,這樣就可以知道系統中任何對象的類型。
參考:《CLR via C#》