構造方法可以重寫嗎_C#學習筆記09--構造方法/析構方法/繼承/多型/封裝
技術標籤:構造方法可以重寫嗎
一.構造方法
當建立一個類時, 系統會自動建立一個預設的無參構造方法, 這個方法和類同名, 在建立物件的時候自動呼叫. 預設的構造方法裡面什麼也沒有, 重寫之後就可以在建立物件的時候同時搞點事情了.
構造方法主要負責對類裡面的欄位進行初始化。
1.1 格式:
public 類名(形參) { 程式碼段; } // 栗子 class Person { public string name; public int age; public string gender; public Person(string name, int age, string gender) { this.name = name; this.age = age; this.gender = gender; } }
1.2 注意事項:
- 構造方法必須要和類名重名;
- 構造方法沒有返回值並且不加void, 但是可以有引數;
- 構造方法可以有多個過載;
- 構造方法只能在建立物件的時候才可以呼叫, 不能手動呼叫;
- 如果有帶引數的構造方法, 想要呼叫預設構造必須顯式的寫出來;
二.析構方法
2.1 使用: 物件銷燬之前都會自動呼叫析構方法
形象的來說, 我們玩遊戲的時候把boss打死之後, boss會爆出各種裝備金幣,藍啊之類的, 這些方法我們就可以在析構方法裡面寫;
2.2 語法:
~類名() { 程式碼段; } // 栗子 ~Person() { Console.WriteLine("我還會回來的......"); }
2.3 注意:
- 只有類才有析構方法, 一個類只能有一個析構方法.
- 無法繼承和過載析構方法;
- 不能手動呼叫, 物件在被銷燬之前會自動呼叫;
- 析構方法沒有訪問修飾符, 沒有引數;
- 必須和類名同名;
三.繼承
3.1 概念:
繼承用於建立可重用、擴充套件和修改在其他類中定義的行為的新類。 其成員被繼承的類稱為“基類”,繼承這些成員的類稱為“派生類”。 派生類只能有一個直接基類(C#中))。 但是,繼承是可傳遞的。 如果 ClassB 派生出 ClassC,ClassA 派生出 ClassB,則 ClassC 會繼承 ClassB 和 ClassA 中宣告的成員。
3.2 格式:
class 子類類名 : 父類類名 { 子類新增加的成員; }
學過構造方法之後, 就想給每個類都寫個構造方法,以便簡化初始化操作. 但是嘗試過後發現如果父類寫了帶引數的構造方法, 子類就會報錯了, 如何解決? base帶你飛.
3.3 繼承父類的構造方法:
孩子在建立物件的時候, 會先呼叫父類的構造方法, 如果父類是無參的就沒有影響, 但是如果父類有有引數的構造方法, 孩子就需要傳引數. 呼叫父類有參的構造方法有兩個方法:
- 在父類中顯式的將預設構造寫出來;
- 子類在重寫構造方法的時候利用base關鍵字傳入父類的引數;
// 父類有參的構造方法
public Zomby(int blood, int attackPower, int speed)
{
this.blood = blood;
this.attackPower = attackPower;
this.speed = speed;
}
//子類有參的構造方法
// base後面要將父類有的引數寫上
public OstableZomby(int blood, int attackPower, int speed, int defense):base(blood, attackPower, speed)
{
this.defense = defense;
}
3.4 注意:
- 繼承具有單向性, a -> b , b不能繼承a;
- C#中,只能繼承一個父類但是可以多重繼承;
- 父類也叫基類, 子類也叫派生類;
- 繼承具有傳遞性; a -> b, b -> c a就具有了c和b所有屬性和方法;
- 基類有的,派生類都有;派生類有的,基類可能沒有。即:派生類可以賦值給基類,而基類不能賦值給派生類。
子類在繼承的時候不管想不想都會繼承父類的所有欄位和方法, 那麼如果子類想用同樣的方法搞特殊呢? 那就要引入多型了.
四.多型
栗子: 就拿我們建立的殭屍們來說, 殭屍都會攻擊, 但是普通殭屍(小口咬), 路障殭屍(大口咬)和鐵桶殭屍(超大口咬)的攻擊方法不同, 這就是多型;
4.1重寫父類方法的多型:
1.在訪問修飾後面加上new關鍵字後重寫:
// 父類的Attack方法
public void Attack()
{
Console.WriteLine("殭屍在攻擊");
}
// 子類重寫Attack方法
// 加不加new都可以
public new void Attack()
{
Console.WriteLine("路障殭屍在攻擊");
}
那麼問題來了, 我雖然重寫了父類的Attack方法, 但是我在某個地方還是想要使用父類的Attack方法的時候怎麼辦呢?
加上 base關鍵字:
// 子類中
public void ObjAttack()
{
// 加上 base關鍵之之後呼叫的就是父類的攻擊方法
base.Attack();
}
//Main函式
OstableZomby ozb = new OstableZomby(150, 20, 20, 50);
ozb.Attack();
ozb.ObjAttack();
小結base:
- 呼叫基類被派生類重寫的方法;
- 指定呼叫基類中某一個指定的構造方法;
4.2 virtual override 的多型
用這種方法來實現多型需要做兩件事情:
1> 將父類中要實現多型性的方法改成虛方法, 即在返回值型別前加關鍵字 virtual:
// 殭屍父類
// 宣告virtual方法的目的是允許子類重寫父類方法實現自己的特性
public virtual void Attack()
{
Console.WriteLine("殭屍在攻擊");
}
2>子類重寫父類的方法, 即將方法隱藏的關鍵字 new 改為 override.
// 路障殭屍子類
// 方法重寫一定要配合virtual方法一起使用, 也就是宣告override的方法, 子類中一定要存在一個同樣的virtual方法
public override void Attack()
{
Console.WriteLine("路障殭屍在攻擊");
}
// 鐵通殭屍子類
public override void Attack()
{
Console.WriteLine("鐵桶殭屍在攻擊");
}
假如說現在有20個路障殭屍和10個鐵桶殭屍, 他們現在都要進行攻擊怎麼實現? 藉助陣列唄. 那麼問題又來了, 陣列型別是什麼? 陣列中既有路障殭屍類又有鐵桶殭屍類, 那想著能不能定義成他們的父類普通殭屍類呢? 要解決這個問題就需要用到里氏轉換了.
4.3里氏轉換
里氏轉換的本質其實就是將父類強制轉換成子類.
4.3.1 里氏轉換第一原則
子類物件可以直接賦值給父類物件
// Main函式中
Zomby zb = new OstableZomby();
// 將子類物件賦值給父類物件, 此時會存在隱式轉換
// 隱式轉換會導致部分資料丟失, 此時Zomby 只能呼叫 Zomby類中的成員了;
4.3.2 里氏轉換第二原則
父類可以通過強制轉換的方式轉換成子類, 此時就可以呼叫子類中的成員了
- 使用is關鍵字判斷型別
- 使用 as 關鍵字進行強制轉換
// Main函式中
// 判斷物件是不是 ObstableZomny 型別
if(zb is OstableZomby)
{
OstableZomby ozb = zb as OstableZomby;
}
使用 as 關鍵字進行強轉即使不成功也不會報錯, 而是返回 null, 但是強行的 "ozb = (OstableZomby)zb" 轉換如果不成功的話, 程式就會報錯終止.
那麼現在我們就是將陣列問題解決了:
// Main函式中
// 先定義30個物件, 我就簡單寫了
Zmoby zb = new Zomby();
OstableZomby ozb = new OstableZomby();
MetalZomby mzb = new MetalZomby();
OstableZomby ozb1 = new OstableZomby();
MetalZomby mzb1 = new MetalZomby();
OstableZomby ozb2 = new OstableZomby();
MetalZomby mzb2 = new MetalZomby();
OstableZomby ozb3 = new OstableZomby();
MetalZomby mzb3= new MetalZomby();
OstableZomby ozb4 = new OstableZomby();
MetalZomby mzb4 = new MetalZomby();
// 定義父類型別的陣列並初始化
Zmoby[] zbs = { ozb, mzb, ozb1, mzb1, ozb2, mzb2, ozb3, mzb3, ozb4, mzb4 };
// 里氏轉化並列印
foreach (Zmoby item in zbs)
{
if (item is OstableZomby)
{
OstableZomby o = item as OstableZomby;
o.Attack();
}
if (item is MetalZomby)
{
MetalZomby o = item as MetalZomby;
o.Attack();
}
}
五.封裝
在C#中, 可以把類裡面的這些欄位進行私有化保護, 併為其新增共有屬性, 在類的外面不能通過物件直接訪問這些欄位, 想要訪問只能通過訪問屬性的方式來完成, 這就是封裝. 具體操作在記錄屬性的時候其實就已經寫了, 這裡就不再記錄了.
補充.結構體和類的區別:
- 結構體是值型別, 而類是引用型別;
- 結構體只能有兩個構造方法, 一個是無參預設的(且不能顯式寫出來!), 一個是將所有欄位都初始化的;
- 結構體不能被繼承, 而類可以被繼承;
- 類有多型性, 結構體沒有多型性;
- 一般只操作一些資料或是以資料為主的用結構型別, 當表示有一些層級關係的時候用類;
- 結構體有裝箱拆箱的操作(值型別), 而類沒有裝箱拆箱的操作(引用型別);
int a = 20;
object obj = (oject)a; // 裝箱操作 值型別轉換為引用型別
int b = (int)obj; // 拆箱操作 引用型別轉換為值型別
=======================================================
思維導圖