最近由於出差在外地,再加上自己在學習一些新的知識,所以遲遲沒有再看這本書,更沒有更新此系列的後續內容,但是這本書確實是本好書,不想就此放下,碰巧今天也比較悠閒,於是今天又一次回顧了之前的內容,並開始了新的閱讀。今天開始盡量盡快進行更新。
在開始之前,我想先講一下繼承的概念。大家都知道,面向對象是由:封裝、繼承、多態來體現的,至於什麼是封裝,不是很明白的朋友請去Google找下答案吧。那麼,什麼事繼承?“繼承”我們可以理解為是一種在無需重新編寫類本身的情況下,對該類進行擴展的能力,並且可以使用現有類的所有功能。那麼,通過繼承創建的新類,我們可以稱之為:“子類”或者“派生類”,反之,被繼承的類我們稱之為:“父類”或者“基類”,子類可以繼承多個父類(派生類可以繼承多個基類)但是一般情況下,一個子類只能有一個父類,要實現多重繼承,可以通過多級繼承來實現。繼承的概念,筆者就先簡單的介紹到這裡吧,如果還是有些不明白的讀者,可以在網上找些資料,相信博客園裡面也有對繼承概念有專門講解的博文。
在我們C#或者JAVA中,不管我們自己創建、調用任何一個類,它的頂級父類只有System.Object這一個類。也就是說,所有的類都是通過它進行衍生的。在我們創建完一個類的時候,類裡面任何方法、字段、屬性都不去寫,在另一個類中進行實例化之後,都會看到一些方法如:Equals();GetHashCode();ToString();GetType()等。那麼這些方法就是System.Object類中的方法。這就是一個繼承的概念,在我們創建一個類的時候,它繼承了System.Object類、擁有了System.Object類中的方法,並且對System.Object類進行了一個擴展。上面我提到一個實例化。我們都是用new關鍵字來對一個類進行實例化(非靜態類(static))在我們new了一個實例化的時候,會自動在堆上為我們創建一個“類型對象指針”和“同步索引快”,而且CLR會自動給我們分配一個該類所需要的內存,這個內存的大小根據類中的字段所需要的字節數進行設置,然而,最終new關鍵字會調用System.Object的構造函數,並進行返回,會返回給新對象一個引用(對象指針),我們所有調用類中的方法,屬性的時候,是根據這個引用的內存地址進行操作的。也可以理解為我們操作內存(實際上是CLR幫我們完成的),到這裡可能會有一個問題,就是,這個對象的內存我們 如何去釋放。這就輪到我們CLR的垃圾回收機制(GC)進行檢測,並對不再訪問的內存進行自動釋放操作。垃圾回收機制將在基本內容更新過以後進行更新。
在我們日常的使用中,會非常頻繁用到類型的轉換。那麼類型是可以隨便進行轉換的麼(比如Person類型轉換成DateTime類型)?答案是不可以。可是為什麼不可以?首先,CLR最重要的特性之一就是安全性。這個安全性就已經把我們限制死了。在我們使用反射的時候會用到GetType()方法,這個方法是一個非虛方法,是不可重寫的方法,這個方法不可重寫,就意味著我們不能把Person類通過GetType方法偽裝成DateTime類型,並進行返回。在類型轉換的時候,CLR准許我們將一個父類強制轉換成它的子類,而不需要任何特殊的語法把子類轉換成父類,例如:
namespace Chapter4
{
class Program
{
static void Main(string[] args)
{
//子類轉換成父類不需要強制轉換
Object o = new Person();
//父類轉換成子類必須進行強制類型轉換
Person p = (Person)o;
}
}
//該類繼承自System.Object類
internal class Person
{
private string name;
private int age;
}
}
那麼兩個類都集成自Object類,為什麼就不能相互進行轉換?CLR在每次進行轉換的時候,都會對轉換的對象進行檢查,確定o是否引用自Peron對象,或者是否是從Peron類派生 的任何子類,如果是CLR將准許轉換,否則將拋出異常,就拿剛才說的Peron轉換成DateTime為例,代碼如下:
class Program
{
static void Main(string[] args)
{
//子類轉換成父類不需要強制轉換
Object o = new Person();
//父類轉換成子類必須進行強制類型轉換
Person p = (Person)o;
//將Person的子類轉換成Person
Man m = new Man();
MagicPerson(m);
//構建一個DateTime對象,並轉換成Person
DateTime dtNow = DateTime.Now;
MagicPerson(dtNow);
}
private static void MagicPerson(object o)
{
Person p = (Person)o;
}
}
//該類繼承自System.Object類
internal class Person
{
private string name;
private int age;
}
//該類繼承自Person(最高基類為System.Object)
internal class Man : Person
{
private string sex;
}
以上的代碼完全可以通過編譯,並運行。那麼第二次運行到MagicPerson方法的時候,CLR會給我們拋除一個為:System.InvalidCastExiception異常,並告訴我們轉換失敗。為什麼會轉換失敗?我們來分析一下CLR進行類型轉換時候的工作步驟。首先在編譯時,編譯器並不知道MagicPerson中參數o到底是誰的引用,所以編譯器准許代碼編譯通過。但在運行時,CLR知道了第一次進來的o是Man的引用,並繼承自Person類,第一次轉換成功。當再次進入MagicPerson方法的時候對象的引用為DateTime的引用。CLR會核實DateTime類型是否繼承自Person或者是否是Peron類型,發現並不繼承自Person類也是Person類型,便拋出異常信息終止轉型。
在以上的代碼例子中,MagicPerson方法在實際運用中參數應該設定為Person類型,這樣的話,編譯器就會報錯,在造成更大損失之前,把罪惡的種子掐死在搖籃之中。
先去吃飯,吃飯回來之後接著更新。
作者 LouisLee