相信只要有在寫程式的人,一定都有聽過物件導向這四個字,尤其是寫 C# 的人一定也曉得 C# 本身就是物件導向程式語言。但聽過歸聽過,還是有許多人仍不曉得物件導向的四大特性,尤其是初學者,所以今天就來說說這四大特性,因為這是很重要的觀念,只要您今天是寫 C#,甚至希望您日後的程式碼能夠好維護的話,更是不能不知道。至於物件導向的由來或是介紹之類的,在這篇文章中不會有任何的介紹,有興趣的人請直接Google,因為在這篇文章主要是直接帶您認識這四大特性!

物件導向四個特性: 

1. 抽象(Abstraction) 

抽象其實不要把它想的太複雜,簡單地說就是把真實世界的需求轉換成類別,而這個類別可以包含狀態(屬性)或是行為(方法)。
例如真實世界中的汽車,汽車的屬性會有廠牌、顏色、速度等等,那汽車的行為不外乎就是加速、剎車,所以在大致分出汽車的屬性與行為後,就可以依照屬性與行為來設計一個汽車的類別。設計好汽車類別後,這個汽車類別就是一個藍圖,需要使用它的時,只要將它 new 出來,就可以針對 new 出來的汽車物件屬性或方法來進行存取。

程式範例
依照汽車的屬性與行為,設計一個 Car 類別並使用 Car 類別生成一個汽車物件後,使用它的屬性與方法。

 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
class Program
{
    public static void Main(string[] args)
    {
        Car car = new Car();
        car.Brand = "Honda";
        car.Color = "Blue";
        car.Speed = 60;

        Console.WriteLine($"建立一台汽車,廠牌:{car.Brand},顏色:{car.Color},目前速度:{car.Speed}。");
        Console.ReadKey();
    }
}

public class Car
{
    public string Brand { get; set; }
    public string Color { get; set; }
    public int Speed { get; set; }

    public void Accelerate()
    {

    }

    public void Brake()
    {

    }
}

執行結果 "New Car Class"

2. 封裝(Encapsulation) 

隱藏或保護內部實作的細節,並且可以透過存取層級(Public、Private、Protected)來設定屬性或方法,像汽車擁有加速以及剎車的方法,對外只是提供加速與剎車的方法,但外部的使用者不用知道它的加速和剎車方法是如何實作,以剛剛所建立的汽車類別來看,我們透過實作加速和剎車方法來示範,除了要讓汽車可以加速外,我們也要限制它的速限,限制它的最高時速只能到達200,剎車減速不可以將速度減至小於0,透過這樣的限制,即使外部使用者傳入了超過規定的值,我們依然可以明確的把傳入值控制在我們要的範圍內。

程式範例
實作汽車的加速、剎車方法,即使加速我輸入超過200的值,它也只能加速至我設定的200,剎車輸入大於200的值,也只能將速度歸0。

 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
36
37
38
39
40
41
42
43
44
45
46
47
class Program
{
    public static void Main(string[] args)
    {
        Car car = new Car();
        car.Brand = "Honda";
        car.Color = "Blue";
        car.Speed = 60;

        Console.WriteLine($"建立一台汽車,廠牌:{car.Brand},顏色:{car.Color},目前速度:{car.Speed}。");
        Console.WriteLine();
        Console.WriteLine($"進行加速,輸入要加速的速度?");
        var speed = Convert.ToInt32(Console.ReadLine());
        car.Accelerate(speed);
        Console.WriteLine($"汽車加速至{car.Speed}");
        Console.WriteLine();
        Console.WriteLine($"進行減速,輸入要減速的速度?");
        speed = Convert.ToInt32(Console.ReadLine());
        car.Brake(speed);
        Console.WriteLine($"汽車減速至{car.Speed}");
    }
}

public class Car
{
    public string Brand { get; set; }
    public string Color { get; set; }
    public int Speed { get; set; }

    public void Accelerate(int speed)
    {
        Speed += speed;
        if (Speed > 200)
        {
            Speed = 200;
        }
    }

    public void Brake(int speed)
    {
        Speed -= speed;
        if (Speed < 0)
        {
            Speed = 0;
        }
    }
}

執行結果 封裝

3. 繼承(Inheritance) 

繼承在 C# 中也是相當重要的一環,繼承可讓您建立的類別被重複使用、擴充和修改類別中所定義的方法。
被繼承的類別稱為「基底類別」(Base Class),而繼承「基底類別」的類別稱之為「衍生類別」(Derived Class),也有人會將「基底類別」稱之為「父類別」,「衍生類別」稱為「子類別」,其中要注意的是 C# 不支援多重繼承,所以一個「衍生類別」只能繼承一個「基底類別」。

程式範例
回到剛剛汽車的例子,將 Car 做為「基底類別」,只保留汽車共同的屬性與方法,另外建立 Honda 與 Nissan 的「衍生類別」並繼承 Car 「基底類別」,當今天需要使用到 Honda 與 Nissan 這二個廠牌的汽車物件時,只要直接 new 出 Honda 與 Nissan,就可以擁有汽車的屬性與方法。

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class Program
{
    public static void Main(string[] args)
    {
        var speed = 0;

        Honda honda = new Honda();
        honda.Color = "Red";
        honda.Speed = 60;

        Nissan nissan = new Nissan();
        nissan.Color = "Blue";
        nissan.Speed = 80;

        Console.WriteLine($"建立一台汽車,廠牌:{honda.GetType().Name},顏色:{honda.Color},目前速度:{honda.Speed}。");
        Console.WriteLine();
        Console.WriteLine($"建立一台汽車,廠牌:{nissan.GetType().Name},顏色:{nissan.Color},目前速度:{nissan.Speed}。");
        Console.WriteLine();

        Console.WriteLine($"Honda進行加速,輸入要加速的速度?");
        speed = Convert.ToInt32(Console.ReadLine());
        honda.Accelerate(speed);
        Console.WriteLine($"Honda汽車加速至{honda.Speed}");
        Console.WriteLine();

        Console.WriteLine($"Nissan進行加速,輸入要加速的速度?");
        speed = Convert.ToInt32(Console.ReadLine());
        nissan.Accelerate(speed);
        Console.WriteLine($"Nissan汽車加速至{nissan.Speed}");
        Console.WriteLine();

        Console.WriteLine($"Honda進行減速,輸入要減速的速度?");
        speed = Convert.ToInt32(Console.ReadLine());
        honda.Brake(speed);
        Console.WriteLine($"Honda汽車減速至{honda.Speed}");
        Console.WriteLine();

        Console.WriteLine($"Nissan進行減速,輸入要減速的速度?");
        speed = Convert.ToInt32(Console.ReadLine());
        nissan.Brake(speed);
        Console.WriteLine($"Nissan汽車減速至{nissan.Speed}");
    }
}

public class Car
{
    public string Color { get; set; }
    public int Speed { get; set; }

    public virtual void Accelerate(int speed)
    {
        Speed += speed;
        if (Speed > 200)
        {
            Speed = 200;
        }
    }

    public void Brake(int speed)
    {
        Speed -= speed;
        if (Speed < 0)
        {
            Speed = 0;
        }
    }
}

public class Honda : Car
{

}

public class Nissan : Car
{

}

執行結果 Inheritance

4. 多型(Polymorphism) 

它可以在相同的介面下,用不同的型別來實現,當「衍生類別」的物件宣告或轉型成「基底類別」的型別時,還可以正確執行該「衍生類別」的行為。其實「多型」與「繼承」息息相關,繼承了「基底類別」的方法並且覆寫成新的方法,「衍生類別」除了可以新增「基底類別」所沒有的方法外(也就是指擴充),若希望覆寫「基底類別」的方法時,則要在「基底類別」的方法加上 virtual 關鍵字,「衍生類別」要覆寫「基底類別」的方法時,則要在方法加上 override 關鍵字,這樣才能覆寫「基底類別」的方法。

MSDN 對多型的解釋:

基底類別可以定義和實作 virtual「方法」 ,而衍生類別可以覆寫這些方法,換句話說,衍生類別會提供自己的定義和實作。 在執行階段,當用戶端程式碼呼叫方法時,CLR 會查詢物件的執行階段類型,然後叫用虛擬方法的覆寫。 因此,在您的原始程式碼中,您可以在基底類別上呼叫方法,然後執行衍生類別版本的方法。

簡單的說明就是…

設計時期(Design Time)

  • 「基底類別」可以定義和實作『虛擬』屬性和方法(virtual)。
  • 「衍生類別」可以覆寫「基底類別」的『虛擬』屬性和方法(override)。

所以在執行時期(Runtime),當呼叫「基底類別」的『虛擬』方法時,會改呼叫「衍生類別」覆寫的方法。要知道,在 C# 中所有的類型都是多型,因為所有的類型均是繼承自 Object 類別。

程式範例 1
在另外設計的 Honda「衍生類別」中,覆寫「基底類別」的加速方法,因為我希望 Honda 的最高時速可以到達 230,另外再擴充一個渦輪加速方法。

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class Program
{
    public static void Main(string[] args)
    {
        var speed = 0;

        Honda honda = new Honda();
        honda.Color = "Red";
        honda.Speed = 60;

        Nissan nissan = new Nissan();
        nissan.Color = "Blue";
        nissan.Speed = 80;

        Console.WriteLine($"建立一台汽車,廠牌:{honda.GetType().Name},顏色:{honda.Color},目前速度:{honda.Speed}。");
        Console.WriteLine();
        Console.WriteLine($"建立一台汽車,廠牌:{nissan.GetType().Name},顏色:{nissan.Color},目前速度:{nissan.Speed}。");
        Console.WriteLine();

        Console.WriteLine($"Honda進行加速,輸入要加速的速度?");
        speed = Convert.ToInt32(Console.ReadLine());
        honda.Accelerate(speed);
        Console.WriteLine($"Honda汽車加速至{honda.Speed}");
        Console.WriteLine();
        honda.Turbo();
        Console.WriteLine($"Honda汽車開啟 Turbo,目前速度:{honda.Speed}");
    }
}

public class Car
{
    public string Color { get; set; }
    public int Speed { get; set; }

    public virtual void Accelerate(int speed)
    {
        Speed += speed;
        if (Speed > 200)
        {
            Speed = 200;
        }
    }

    public void Brake(int speed)
    {
        Speed -= speed;
        if (Speed < 0)
        {
            Speed = 0;
        }
    }
}

public class Honda : Car
{
    public override void Accelerate(int speed)
    {
        Speed += speed;
        if (Speed > 230)
        {
            Speed = 230;
        }
    }

    public void Turbo()
    {
        Speed += 30;
    }
}

public class Nissan : Car
{

}

執行結果
Polymorphism

程式範例 2
多型另一種使用方式,可以透過「基底類別」來建立一個集合,但放入集合中的物件都是「衍生類別」的型別,例如今天我不曉得使用者在編譯時期會建立那種汽車物件,但程式必需要能夠正確的執行該傳入物件的屬性與方法,這時就可以透過多型來解決。

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Program
{
    public static void Main(string[] args)
    {
        var speed = 0;

        List<Car> cars = new List<Car>();

        cars.Add(new Honda
        {
            Color = "Red",
            Speed = 60
        });

        cars.Add(new Nissan
        {
            Color = "Blue",
            Speed = 80
        });

        foreach (var car in cars)
        {
            Console.WriteLine($"建立一台汽車,廠牌:{car.GetType().Name},顏色:{car.Color},目前速度:{car.Speed}。");
            Console.WriteLine();
            Console.WriteLine($"{car.GetType().Name}進行加速,輸入要加速的速度?");
            speed = Convert.ToInt32(Console.ReadLine());
            car.Accelerate(speed);
            Console.WriteLine($"{car.GetType().Name}汽車加速至{car.Speed}");
            Console.WriteLine();
        }
    }
}

public class Car
{
    public string Color { get; set; }
    public int Speed { get; set; }

    public virtual void Accelerate(int speed)
    {
        Speed += speed;
        if (Speed > 200)
        {
            Speed = 200;
        }
    }

    public void Brake(int speed)
    {
        Speed -= speed;
        if (Speed < 0)
        {
            Speed = 0;
        }
    }
}

public class Honda : Car
{
    
}

public class Nissan : Car
{

}

執行結果
Polymorphism

上述就是物件導向四種特性的說明與示範,初學者一定要了解,當然實際運用上,可不只這樣,還會搭配 SOLID 原則,不過那又是另一個議題了,至少先搞懂這四個特性吧。