回顧 虛方法 virtural、抽象方法 abstract
虛方法 virtural
關於虛方法需要注意的幾點:
- 父類中如果有方法需要讓子類重寫,則可以將該方法標記為
virtual - 虛方法在父類中必須有實現,哪怕是空實現
什麼是空實現?有大括號,沒有內容void T() { }
你有了大括號就叫做有方法體,你裡面什麼都不寫,叫空實現
1
2
3
4
5
6
//如果我這個方法需要被子類重寫,可以標記為虛方法virtual
public virtual void SayHello()
{
//虛方法,可以空實現
//有方法體(大括號{}),沒有內容,什麼都不寫
}
有沒有方法體?有,因為有大括號
{}
有沒有實現?沒有,因為沒有內容,什麼都不寫
- 虛方法子類可以重寫(
override),也可以不重寫
子類可以重寫(override)父類方法
1 2 3 4 //子類可以重寫(override)父類方法 public override void SayHello() { base.SayHello(); }也可以不重寫父類方法
1 2 3 //也可以不重寫父類方法(沒有override標記) public void SayHello() { }但是,如果此類是抽象類別,子類必須要重寫父類的抽象成員
1 2 3 public abstract class Person { //抽象類別 public abstract void Test(); //抽象函數 => 子類必須要重寫該方法 }
抽象方法 abstract
關於抽象方法需要注意的幾點:
- 需要用
abstract關鍵字標記 - 抽象方法不能有任何方法實現
1 2
//抽象方法 加上abstract,沒有方法體 public abstract int Test(string name);
- 抽象成員必須在抽象類別中 抽象成員必須在抽象類別中,但是抽象類別當中可以有非抽象成員,同樣可以寫字段、屬性、函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
//抽象類別 public abstract class Person { //抽象類別當中可以有非抽象成員,同樣可以寫構造函數、字段、屬性、函數 //但,這些成員是給自己用的,純粹是給子類用的(給繼承用) public Person(string name) { this.Name = _name; } //會報錯是因為沒有無參的構造函數,再加上去就可以了 public Person() { } private string _name; public string Name { get { return _name; } set { value = Name; } } //抽象方法 public abstract int Test(string name); public virtual void SayHello() { } }
- 由於抽象成員沒有任何實現,所以子類必須將抽象成員重寫。
1 2 3 4 5 6 7 8 9 10 11
//抽象成員只允許在抽象類別當中,不能在普通類別中有抽象成員 public abstract class Person { //抽象方法不能有任何方法實現(沒有方法體) public abstract int Test(string name); } //子類在重寫的時候,必須跟父類的這個抽象方法具有相的簽名(返回值和參數一樣) public class Student : Person { public override int Test(string name) { return 123; } }
- 抽象類別不能實體化
- 抽象類的作用,抽象類別的作用就是為了讓子類繼承
- 抽象類別中可以包括抽象成員、可以包括有具體有代碼的成員
- 抽象方法不能用
static修飾
Q:為什麼 抽象方法不能是靜態的static?
我們調用函數的時候,是拿子類物件去調的,這樣搞的話,類名是不是也可以去調用了(因為靜態類是用類名去調用函數),所以他不允許你這樣子,你必須拿對象(物件)去調這個函數。
Interface 介面
- 介面中只含方法(屬性、事件、索引器也都是方法)
- 介面中的成員都不能有任何實現
介面中的函數,跟抽象類的抽象函數是一樣的。四個字形容:光說不做。
由誰做呀?子類去做。 - 介面不能被實體化
現在我們己知的,不能被實體化的有:介面、抽象類、靜態類 - 介面中的成員不能有任何訪問修飾符。(默認為
public)1 2 3 4 5
public interface IFlyable { //函數前面不能有修飾符 //在介面當中,所有成員默認都是public,也不讓你加public void Fly(); }
- 實現介面的子類必須將介面中的所有成員全都實現(與抽象類相同)
跟抽象類一樣。抽象類裡的所有成員,必須全部都實現。 - 子類實現介面的方法時,不需要任何關鍵字,直接實現即可。
而我們在實現virtual虛方法和abstract抽象函數的時候,都需要加上override,而實現介面的時候不需要。 - 介面存在的意義就是為了多態(多型)
Q:介面是什麼?什麼時候要使用介面 interface? 使用介面的目的是什麼?
介面是什麼?
介面是規範一個能力,尤其是側重於表現於那個能力。
例如:鳥會飛、飛機會飛。「飛」這個能力就可以寫成介面
什麼時候要使用介面 interface?
例如:鳥會飛、飛機會飛,他們是不是都會飛呀?你能提一個父類讓他們去繼承嗎?不能。
鳥會飛、飛機會飛。 他們是不是都有一個會飛的方法,對吧。但是你能不能把「飛」的方法單獨拿出來,封裝成一個父類?不能。有東西可以做為鳥跟飛機的父類嗎?沒有。
所以說,你只能把這個「飛」寫成一個「介面」
1
2
3
4
5
6
public interface IFlyable
{
//函數前面不能有修飾符
//在介面當中,所有成員默認都是public,也不讓你加public
void Fly();
}
使用介面的目的是什麼?
為了多態(多型),表現出多種類型。實現多態(多型)的三種方法:虛方法virtual、抽象abstruce、介面interface。我們在使用介面實現多態(多型)的時候,聲明的也是介面類型,指向的也是誰呀?子類對象。
表面上我調的是我介面裡面自己的Fly,實際上我調的是子類的Fly
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
namespace Interface
{
internal class Program
{
static void Main(string[] args)
{
//介面是什麼?什麼時候要使用介面 `interface`? 使用介面的目的是什麼?多態/多型
//鳥會飛 飛機也會飛
IFlyable fly = new Plane();//new Bird();
fly.Fly();
Console.ReadKey();
}
}
public interface IFlyable
{
//函數前面不能有修飾符
//在介面當中,所有成員默認都是public,也不讓你加public
void Fly();
}
//當你繼承了介面,就必須要實現介面的成員
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("鳥會飛");
}
}
public class Plane : IFlyable
{
public void Fly()
{
Console.WriteLine("飛機會飛");
}
}
}
再一個例子,鳥會飛、飛機會飛,我再加上麻雀會飛,企鵝不會飛。
寫一個鳥類的父類Bird,所有的鳥類都繼承父類,我能在父類裡面提供一個會飛的方法,讓兩個鳥類去繼承嗎?不能,因為不是所有的鳥都會飛,比如說,企鵝就不會飛。這時候,這個「飛」就只能寫成一個介面。
現在這個麻雀應該是,既要繼承於Bird鳥類,也繼承於這個IFlyable介面,那麼在語法上,我們要求把誰寫在前面?把父類寫在前面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//鳥類的父類
public class Bird
{
//因為不是所有的鳥都會飛,所以裡面不能提供「飛的方法」
}
//當你繼承了介面,就必須要實現介面的成員
public class Maque : Bird, IFlyable
{
public void Fly() {
Console.WriteLine("麻雀會飛");
}
}
public class QQ : Bird //企鵝
{
}
public interface IFlyable
{
//函數前面不能有修飾符
//在介面當中,所有成員默認都是public,也不讓你加public
void Fly();
}
再來,下面這段程式碼:
這個函數是父類的?還是子類的?還是實現介面的?
1
2
3
4
5
6
7
8
9
10
public class Maque : Bird, IFlyable
{
/// <summary>
/// 這個函數是父類的?還是子類的?還是實現介面的?
/// </summary>
public void Fly() //我是介面的
{
Console.WriteLine("麻雀會飛");
}
}
一個子類去繼承了一個介面,我們在語法上要求你要子類必須要實現介面中的成員,對吧?如果說,你這個方法是父類的,或者是自己的,但,就不是介面的,那語法上能不能通過?不能。所以說,現在語法上建置成功了,所以說,你這個函數肯定是誰的?介面的。
現在給你報一個警告,是因為你自己提供的這個函數跟父類的函數一樣了,從繼承角度來說,你會隱藏掉你繼承過來的這個函數,對吧。
承上,如果,我想要這個函數表示為我麻雀自己的函數,而不是實現介面的函數,這事我該怎麼去做?
實現介面的函數有兩種方式:
- 普通實現 (實作介面)
- 顯示實現介面 (明確實作所有成員):解決方法重名的問題
顯示實現介面 (明確實作所有成員)只有一個目的,就是解決方法重名的問題。 當你子類中的函數,跟介面中函數名稱一樣的時候,那麼,你在實現這個介面的時候,必須要「顯示實現介面 (明確實作所有成員)」去實現。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Maque : Bird, IFlyable
{
/// <summary>
/// 這個函數是父類的?還是子類的?還是實現介面的?
/// </summary>
public void Fly() //子類自己的
{
Console.WriteLine("麻雀會飛");
}
//介面的,使用顯示實現介面 (明確實作所有成員)」去實現
void IFlyable.Fly()
{
Console.WriteLine("實現介面的飛方法");
}
}