使用基類的引用
派生類的實例由基類的實例和派生類新增的成員組成。派生類的引用指向整個類對象,包括
基類部分。
如果有一個派生類對象的引用,就可以獲取該對象基類部分的引用(使用類型轉換運算符把
該引用轉換為基類類型)。類型轉換運算符放置在對象引用的前面,由圓括號括起的要被轉換成
的類名組成。類型轉換將在第17章闡述。將派生類對象強制轉換為基類對象的作用是產生的變
量只能訪問基類的成員(在被覆寫方法中除外,稍后會討論)。
接下來的幾節將闡述使用對象的基類部分的引用來訪問對象。我們從觀察下面兩行代碼開
始,它們聲明了對象的引用。圖8-6闡明了代碼,并展示了不同變量所看到的對象部分。
- 第一行聲明并初始化了變量derived,它包含一個MyDerivedClass類型對象的引用。
- 第二行聲明了一個基類類型MyBaseClass的變量,并把derived中的引用轉換為該類型,
給出對象的基類部分的引用。- 基類部分的引用被存儲在變量mybc中,在賦值運算符的左邊。
- 基類部分的引用“看不到"派生類對象的其余部分,因為它通過基類類型的引用“看”
這個對象。
MyDerivedClass derived=new MyDerivedClass(); //創建一個對象
MyBaseClass mybc=(MyBaseClass) derived; //轉換引用
下面的代碼展示了兩個類的聲明和使用。圖8-7闡明了內存中的對象和引用。
Main創建了一個MyDerivedClass類型的對象,并把它的引用存儲到變量derived中。Main
還創建了一個MyBaseClass類型的變量,并用它存儲對象基類部分的引用。當對每個引用調用
print方法時,調用的是該引用所能看到的方法的實現,并產生不同的輸出字符串。
class MyBaseClass
{public void Print(){Console.WriteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass
{public int var1;new public void Print(){Console.WriteLine("This is the derived class.");}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived; //轉換為基類derived.Print(); //從派生類部分調用Printmybc.Print(); //從基類部分調用Print//mybc.var1=5; //錯誤:基類引用無法訪問派生類成員}
}
虛方法和覆寫方法
在上一節我們看到,當使用基類引用訪問派生類對象時,得到的是基類的成員。虛方法可以
使基類的引用訪問“升至“派生類內。
可以使用基類引用調用派生類的方法,只需滿足下面的條件。
- 派生類的方法和基類的方法有相同的簽名和返回類型。
- 基類的方法使用virtual標注。
- 派生類的方法使用override標注。
例如,下面的代碼展示了基類方法和派生類方法的virtual及override修飾符。
class MyBaseClass //基類
{virtual public void Print()....
}class MyDerivedClass:MyBaseClass //派生類
{override void Print()}
圖8-8闡明了這組virtual和override方法。注意和上一種情況(用new隱藏基類成員)相
比在行為上的區別。
- 當使用基類引用(mybc)調用Print方法時,方法調用被傳遞到派生類并執行,因為:
- 基類的方法被標記為virtual;
- 在派生類中有匹配的override方法。
- 圖8-8闡明了這一點,顯示了一個從virtual Print方法后面開始,并指向overridePrint
方法的箭頭。
下面的代碼和上一節中的相同,但這一次,方法上標注了virtual和override。產生的結果
和前一個示例有很大不同。在這個版本中,對基類方法的調用實際調用了子類中的方法。
class MyBaseClass
{virtual public void Print(){Console.WiteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass
{public override void Print(){Console.WriteLine("This is the derived class.");}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived; //強制轉換成基類derived.Print();mybc.Print();}
}
其他關于virtual和override修飾符的重要信息如下。
- 覆寫和被覆寫的方法必須有相同的可訪問性。例如,這種情況是不可以的:被覆寫的方
法是private的,而覆寫方法是public的。 - 不能覆寫static方法或非虛方法。
- 方法、屬性和索引器(前一章闡述過),以及另一種成員類型一一事件(將在后面闡述),
都可以被聲明為virtual和override。
覆寫標記為override的方法
覆寫方法可以在繼承的任何層次出現。
- 當使用對象基類部分的引用調用一個被覆寫的方法時,方法的調用被沿派生層次上溯執
行,一直到標記為override的方法的最高派生(most-derived)版本。 - 如果在更高的派生級別有該方法的其他聲明,但沒有被標記為override,那么它們不會
被調用。
例如,下面的代碼展示了3個類,它們形成了一個繼承層次:MyBaseClass、MyDerivedClass
和SecondDerived。所有這3個類都包含名為Print的方法,并帶有相同的簽名。在MyBaseClass
中,Print被標記為virtual。在MyDerivedClass中,它被標記為override。在類SecondDerived
中,可以使用override或new聲明方法Print。讓我們看一看在每種情況下將發生什么。
class MyBaseClass //基類
{virtual public void Print(){COnsole.WriteLine("This is the base class.");}
}class MyDerivedClass:MyBaseClass //派生類
{override void Print(){Console.WriteLine("This is the derived class.");}}class SecondDerived:MyDerivedClass //最高派生類
{...//在后面給出
}
情況1:使用override聲明Print
如果把SecondDerived的Print方法聲明為override,那么它會覆寫方法的兩個低派生級別的
版本,如圖8-9所示。如果一個基類的引用被用于調用Print,它會向上傳遞,一直到類secondDerived
中的實現。
下面的代碼實現了這種情況。注意方法Main的最后兩行代碼。
- 兩條語句中的第一條使用最高派生類SecondDerived的引用調用Print方法。這不是通過
基類部分的引用的調用,所以它將會調用SecondDerived中實現的方法。 - 而第二條語句使用基類MyBaseClass的引用調用Print方法。
class SecondDerived:MyDerivedClass
{override public void Print(){Console.WriteLine("This is the second derived class.");}
}class Program
{static void Main(){SecondDerived derived=new SecondDerived();//使用SecondDerivedMyBaseClass mybc=(MybaseClass)derived; //使用MyBaseClassderived.Print();mybc.Print();}
}
結果是:無論Print是通過派生類調用還是通過基類調用的,都會調用最高派生類中的方法。
當通過基類調用時,調用沿著繼承層次向上傳遞。這段代碼產生以下輸出:
This is the second derived class.
This is the second derived class.
2.情況2:使用new聲明Print
相反,如果將SecondDerived中的Print方法聲明為new,則結果如圖8-10所示。Main和上
一種情況相同。
class SecondDerived:MyDerivedClass
{new public void Print(){Console.WriteLine("This is the second derived class.");}
}class Program
{static void Main(){SecondDerived derived=new SecondDerived();//使用SecondDerivedMyBaseClass mybc=(MyBaseClass)derived; //使用MyBaseClassderived.Print();mybc.Print();}
}
結果是:當通過SecondDerived的引用調用方法Print時,SecondDenved中的方法被執行,
正如所期待的那樣。然而,當通過MyBaseClass的引用調用Print方法時,方法調用只向上傳遞
了一級,到達類MyDerived,在那里它被執行。兩種情況的唯一不同是SecondDerived中的方法
使用修飾符override還是修飾符new聲明。
這段代碼產生以下輸出:
This is the second derived class.
This is the derived class.
覆蓋其他成員類型
在之前的幾節中,我們已經學習了如何在方法上使用virtual/override。在屬性、事件以及
索引器上的用法也是一樣的。例如,下面的代碼演示了名為MyProperty的只讀屬性,其中使用
了virtual/override。
class MyBaseClass
{private int _myInt=5;virtual public int MyProperty{get{return _myInt;}}
}class MyDerivedClass:MyBaseClass
{private int _myInt=10;override public int MyProperty {get{return _myInt;}}
}class Program
{static void Main(){MyDerivedClass derived=new MyDerivedClass();MyBaseClass mybc=(MyBaseClass)derived;Console.WriteLine(derived.MyProperty);Console.WriteLine(mybc.MyProperty);}
}