重點1. 類別如何使用繼承之特性(以動物為例)
step1:
尋找所有動物接有的共通事物
step2:
依step1所歸納出之特性,建立一個superclass,提供所有動物的共同特質。
step3:
判斷每一種動物與superclass所提供的方法有何不同?
1.也就是說當你繼承superclass時雖然必須繼承他所有行為,但是你可以進行覆寫(當你的subclass與superclass方法同名,但執行之行為不同時)。
2.當沒有同名方法時,subclass也可以撰寫自己的方法。
step4:
尋找擁有許多共通特質之類別
貓,老虎,獅子都屬於貓科,所以我們可以在這三個class上再加上一個Feline(貓科動物)
step5:
依step4方式建構自己的類別階層結構。
以上步驟做完的好處
1.避免重複之程式碼
2.程式碼變得容易理解以及維護(例如當你在看Feline類別時,你就知道你正在看所有貓科動物的共同特質)。
這與傳統沒有物件導向的程式設計師寫出來的程序導向程式,會差非常多,主要在於debug光是程式跑到這裡是在幹嘛,都會弄個一兩個小時也不誇張。
重點2 :子類別可以覆寫方法,改變或取代他繼承之方法。
有時侯子類別想從覆類別繼承到大多數的方法,但是不是全部,當子類別想要改變繼承到的方法時,可以使用Override。
如何使用?
Step1:
class Bird
{
public virtual void Fly()
{response.write("I can Fly.")}
}
class Penguin : Bird
{
public override void Fly()
{
response.write("i can not fly.")
}
}
note: 覆蓋掉Fly(),注意此覆類別方法沒有回傳值且沒有參數,所以子類別也要是void且沒有參數。
重點3 : 子類別之instance不可以指派給父類別。
ex:
Sandwich 為 BLT 之父類別。
Sandwich mySandwich = new Sandwich();
BLT myBlt = new BLT();
Sandwich someSandwich = myBlt // 正確可以將myBlt指定給任何Snadwich之變數。
BLT anotherBLT = mySandwich; //編譯失敗,並非所有BLT都是Sandwich
重點4: Shadow(遮蔽)
- Public class BaseClass
- {
- //注意這裡沒有使用 virtual 關鍵字
- public string GetMethodOwnerName()
- {
- return "Base Class";
- }
- }
- public class ChildClass : BaseClass
- {
- //注意這裡的 new 關鍵字
- public new string GetMethodOwnerName()
- {
- return "ChildClass";
- }
- }
- static void Main(string[] args)
- {
- ChildClass c = new ChildClass();
- Console.WriteLine(c.GetMethodOwnerName());
- }
OutPut : ChildClass
當父class沒有使用 virtual 關鍵字,而子類別要使用 new 關鍵字時,會將父類別的同名方法給遮蔽掉。
與override非常相似,但是撰寫程式時我們不會事先知道那些要加override,所以也可用此種方式萬一父class沒加 virtual,可以在子class加上new,達到覆蓋同效果。
重點5 : 方法多載(overloading)
假設希望一個Car3 class中能有多種加速方式,如下:
Accelerate();
Accelerate(50);
Accelerate("STOP");
因為上述Accelerate方法參數個數與資料型別不同,所以可以使用overloading來達成。
ex.
Car3 class 檔案:
public class Car3
{
private int speed = 0;
public int Speed
{
get { return speed; }
set
{
if (value <= 0)
speed = 0;
if (value > 1)
speed = 200;
}
}
public void Accelerate()
{
this.Speed++;
}
public void Accelerate(int addSpeed)
{
this.Speed += addSpeed;
}
public void Accelerate(String s)
{
if (s == "STOP") this.Speed = 0;
}
}
.cs檔案:
protected void Page_Load(object sender, EventArgs e)
{
Car3 Benz = new Car3();
Response.Write("現在速度:"+Benz.Speed);
Benz.Accelerate();
Response.Write("現在速度:" + Benz.Speed);
Benz.Accelerate(10);
Response.Write("現在速度:" + Benz.Speed);
Benz.Accelerate("STOP");
Response.Write("現在速度:" + Benz.Speed);
}
結果: 現在速度:0現在速度:0現在速度:200現在速度:0
note: 比較多載(overloading)與覆寫(overriding)
多載成員: 可以接受不同個數之參數或不同資料型別之參數,用來提供不同版本的屬性或方法。
覆寫成員: 子類別之成員可以取代父類別中不適用之成員,子類別覆寫父類別之成員時,子類別和父類別之成員必須接受相同個數參數與相同資料型別。
重點.6 動態繫結(Dynamic Binding)(多型)
Dynamic binding是程式進入執行階段時,物件參考才決定所要執行之方法,其做法是父類別的物件參考來選擇所要執行之子類別物件實體方法,透過此法達到多型。
ex.
Traffic class 有兩個子類別為 Car 以及 Airplane,當輸入1時 Traffic 之參考 r 會去指向Car名為myCar之實體物件,而當輸入2時 r 會去指向Airplane之物件myAirplane
superclass 檔案:
public class Traffic
{
protected static string Name;
public virtual string SpeedUp() {return "father"; }
}
Car class檔案:
public class Car : Traffic
{
public override string SpeedUp()
{
Name = "我是車子";
return Name;
}
}
Airplane class 檔案:
public class Airpalne:Traffic
{
public override string SpeedUp()
{
Name = "我是飛機";
return Name;
}
}
.cs檔案(前端請拉一個textbox跟button即可)
protected void Button1_Click(object sender, EventArgs e)
{
Traffic r; // r為Traffic類別之參考
Car myCar = new Car();
Airpalne myAirplane = new Airpalne();
string input = TextBox1.Text;
if (input == "1")
{
r = myCar; //如果tetxbox輸入為1,r指向myCar物件實體
}
else if (input == "2")
{
r = myAirplane;
}
else {
return;
}
Response.Write(r.SpeedUp()); //看輸入之值決定去呼叫哪一個類別之SpeedUp()
}
結論:
父類別之參考可以動態指向同一類別或子類別之物件實體,進而操作參考所指向之實體方法或屬性,稱之多型。
例如上述 r 指向 myCar 物件實體,則 r 可以操作 myCar物件和 r 相同之屬性或方法。
7.子類別用base關鍵字存取其父類別
假設A class 為 B class 之 父類別。
A的成員變數與方法如下:
1.成員變數: NumberOfLegs
2.方法: Eat(), Swallow(), Diget()
B的成員變數與方法如下:
1.成員變數: TongueLength
2.方法: CatchWithTongue()
ex.
class A
{
public virtual void Eat(Food morsel)
{
Swallow(morsle);
Digest();
}
}
//較長的寫法
calss B : A
{
public override void Eat(Food morsel)
{
CatchWithFood(morsel); //較父類別多了這個
Swallow(morsel);
Digest();
}
}
可以改成下列寫法:
class B : A
{
public override void Eat(Food morsel)
{
CatchWithFood(morsel);
base.Eat(morsel); //因為父類別的Eat()包含Swallow(morsel) and Digest(),所以利用base關鍵字可以使用覆類別之方法。
}
}