上期回顧 -?https://www.cnblogs.com/liu-jinxin/p/10826971.html?
一、類
當你定義一個類時,你定義了一個數據類型的藍圖。這實際上并沒有定義任何的數據,但它定義了類的名稱意味著什么,也就是說,類的對象由什么組成及在這個對象上可執行什么操作。對象是類的實例。構成類的方法和變量成為類的成員。
代碼示例:


1 using System; 2 namespace BoxApplication 3 { 4 class Box 5 { 6 public double length; // 長度 7 public double breadth; // 寬度 8 public double height; // 高度 9 } 10 class Boxtester 11 { 12 static void Main(string[] args) 13 { 14 Box Box1 = new Box(); // 聲明 Box1,類型為 Box 15 Box Box2 = new Box(); // 聲明 Box2,類型為 Box 16 double volume = 0.0; // 體積 17 18 // Box1 詳述 19 Box1.height = 5.0; 20 Box1.length = 6.0; 21 Box1.breadth = 7.0; 22 23 // Box2 詳述 24 Box2.height = 10.0; 25 Box2.length = 12.0; 26 Box2.breadth = 13.0; 27 28 // Box1 的體積 29 volume = Box1.height * Box1.length * Box1.breadth; 30 Console.WriteLine("Box1 的體積: {0}", volume); 31 32 // Box2 的體積 33 volume = Box2.height * Box2.length * Box2.breadth; 34 Console.WriteLine("Box2 的體積: {0}", volume); 35 Console.ReadKey(); 36 } 37 } 38 } 39 //執行結果 40 //Box1 的體積: 210 41 //Box2 的體積: 1560
(一)、成員函數
類的成員函數是一個在類定義中有它的定義或原型的函數,就像其他變量一樣。作為類的一個成員,它能在類的任何對象上操作,且能訪問該對象的類的所有成員。
成員變量是對象的屬性(從設計角度),且它們保持私有來實現封裝。這些變量只能使用公共成員函數來訪問。
代碼示例:


1 using System; 2 namespace BoxApplication 3 { 4 class Box 5 { 6 private double length; // 長度 7 private double breadth; // 寬度 8 private double height; // 高度 9 public void setLength( double len ) 10 { 11 length = len; 12 } 13 14 public void setBreadth( double bre ) 15 { 16 breadth = bre; 17 } 18 19 public void setHeight( double hei ) 20 { 21 height = hei; 22 } 23 public double getVolume() 24 { 25 return length * breadth * height; 26 } 27 } 28 class Boxtester 29 { 30 static void Main(string[] args) 31 { 32 Box Box1 = new Box(); // 聲明 Box1,類型為 Box 33 Box Box2 = new Box(); // 聲明 Box2,類型為 Box 34 double volume; // 體積 35 36 37 // Box1 詳述 38 Box1.setLength(6.0); 39 Box1.setBreadth(7.0); 40 Box1.setHeight(5.0); 41 42 // Box2 詳述 43 Box2.setLength(12.0); 44 Box2.setBreadth(13.0); 45 Box2.setHeight(10.0); 46 47 // Box1 的體積 48 volume = Box1.getVolume(); 49 Console.WriteLine("Box1 的體積: {0}" ,volume); 50 51 // Box2 的體積 52 volume = Box2.getVolume(); 53 Console.WriteLine("Box2 的體積: {0}", volume); 54 55 Console.ReadKey(); 56 } 57 } 58 } 59 //執行結果 60 //Box1 的體積: 210 61 //Box2 的體積: 1560
(二)、構造函數
類的?構造函數?是類的一個特殊的成員函數,當創建類的新對象時執行。
構造函數的名稱與類的名稱完全相同,它沒有任何返回類型。
代碼示例:


1 using System; 2 namespace LineApplication 3 { 4 class Line 5 { 6 private double length; // 線條的長度 7 public Line() //構造函數 8 { 9 Console.WriteLine("對象已創建"); 10 } 11 12 public void setLength( double len ) 13 { 14 length = len; 15 } 16 public double getLength() 17 { 18 return length; 19 } 20 21 static void Main(string[] args) 22 { 23 Line line = new Line(); 24 // 設置線條長度 25 line.setLength(6.0); 26 Console.WriteLine("線條的長度: {0}", line.getLength()); 27 Console.ReadKey(); 28 } 29 } 30 } 31 //執行結果: 32 //對象已創建 33 //線條的長度: 6
默認的構造函數沒有任何參數。但是如果你需要一個帶有參數的構造函數可以有參數,這種構造函數叫做參數化構造函數。這種技術可以幫助你在創建對象的同時給對象賦初始值
代碼示例:


1 using System; 2 namespace LineApplication 3 { 4 class Line 5 { 6 private double length; // 線條的長度 7 public Line(double len) // 參數化構造函數 8 { 9 Console.WriteLine("對象已創建,length = {0}", len); 10 length = len; 11 } 12 13 public void setLength( double len ) 14 { 15 length = len; 16 } 17 public double getLength() 18 { 19 return length; 20 } 21 22 static void Main(string[] args) 23 { 24 Line line = new Line(10.0); 25 Console.WriteLine("線條的長度: {0}", line.getLength()); 26 // 設置線條長度 27 line.setLength(6.0); 28 Console.WriteLine("線條的長度: {0}", line.getLength()); 29 Console.ReadKey(); 30 } 31 } 32 } 33 //執行結果: 34 //對象已創建,length = 10 35 //線條的長度: 10 36 //線條的長度: 6
(三)、析構函數
類的?析構函數?是類的一個特殊的成員函數,當類的對象超出范圍時執行。
析構函數的名稱是在類的名稱前加上一個波浪形(~)作為前綴,它不返回值,也不帶任何參數。
析構函數用于在結束程序(比如關閉文件、釋放內存等)之前釋放資源。析構函數不能繼承或重載。
代碼示例:


1 using System; 2 namespace LineApplication 3 { 4 class Line 5 { 6 private double length; // 線條的長度 7 public Line() // 構造函數 8 { 9 Console.WriteLine("對象已創建"); 10 } 11 ~Line() //析構函數 12 { 13 Console.WriteLine("對象已刪除"); 14 } 15 16 public void setLength( double len ) 17 { 18 length = len; 19 } 20 public double getLength() 21 { 22 return length; 23 } 24 25 static void Main(string[] args) 26 { 27 Line line = new Line(); 28 // 設置線條長度 29 line.setLength(6.0); 30 Console.WriteLine("線條的長度: {0}", line.getLength()); 31 } 32 } 33 } 34 //執行結果: 35 //對象已創建 36 //線條的長度: 6 37 //對象已刪除
構造函數在創建實例時調用,析構函數在結束程序時調用。
(四)、靜態成員
我們可以使用?static?關鍵字把類成員定義為靜態的。當我們聲明一個類成員為靜態時,意味著無論有多少個類的對象被創建,只會有一個該靜態成員的副本。
關鍵字?static?意味著類中只有一個該成員的實例。靜態變量用于定義常量,因為它們的值可以通過直接調用類而不需要創建類的實例來獲取。靜態變量可在成員函數或類的定義外部進行初始化。你也可以在類的定義內部初始化靜態變量。
代碼示例:


1 using System; 2 namespace StaticVarApplication 3 { 4 class StaticVar 5 { 6 public static int num; 7 public void count() 8 { 9 num++; 10 } 11 public int getNum() 12 { 13 return num; 14 } 15 } 16 class StaticTester 17 { 18 static void Main(string[] args) 19 { 20 StaticVar s1 = new StaticVar(); 21 StaticVar s2 = new StaticVar(); 22 s1.count(); 23 s1.count(); 24 s1.count(); 25 s2.count(); 26 s2.count(); 27 s2.count(); 28 Console.WriteLine("s1 的變量 num: {0}", s1.getNum()); 29 Console.WriteLine("s2 的變量 num: {0}", s2.getNum()); 30 Console.ReadKey(); 31 } 32 } 33 } 34 //執行結果 35 //s1 的變量 num: 6 36 //s2 的變量 num: 6
你也可以把一個成員函數聲明為?static。這樣的函數只能訪問靜態變量。靜態函數在對象被創建之前就已經存在。
代碼示例:


1 using System; 2 namespace StaticVarApplication 3 { 4 class StaticVar 5 { 6 public static int num; 7 public void count() 8 { 9 num++; 10 } 11 public static int getNum() 12 { 13 return num; 14 } 15 } 16 class StaticTester 17 { 18 static void Main(string[] args) 19 { 20 StaticVar s = new StaticVar(); 21 s.count(); 22 s.count(); 23 s.count(); 24 Console.WriteLine("變量 num: {0}", StaticVar.getNum()); 25 Console.ReadKey(); 26 } 27 } 28 } 29 //執行結果: 30 //變量 num: 3
將類成員函數聲明為public static無需實例化即可調用類成員函數。
反之,如果不聲明為static,即使和Main方法從屬于同一個類,也必須經過實例化
代碼示例:


1 //示例一: 2 using System; 3 4 namespace ConsoleApp 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int num = AddClass.Add(2, 3); //編譯通過 11 Console.WriteLine(num); 12 } 13 } 14 15 class AddClass 16 { 17 public static int Add(int x,int y) 18 { 19 return x + y; 20 } 21 } 22 } 23 24 //示例二: 25 using System; 26 27 namespace ConsoleApp 28 { 29 class Program 30 { 31 static void Main(string[] args) 32 { 33 int num = Add(2, 3); //編譯錯誤,即使改為Program.Add(2, 3);也無法通過編譯 34 Console.WriteLine(num); 35 } 36 37 public int Add(int x, int y) 38 { 39 return x + y; 40 } 41 } 42 } 43 using System; 44 45 namespace ConsoleApp 46 { 47 class Program 48 { 49 static void Main(string[] args) 50 { 51 Program self = new Program(); 52 int num = self.Add(2, 3); //編譯通過 53 Console.WriteLine(num); 54 } 55 56 public int Add(int x, int y) 57 { 58 return x + y; 59 } 60 } 61 }
注:
我們可以使用?static?關鍵字把類成員定義為靜態的。當我們聲明一個類成員為靜態時,意味著無論有多少個類的對象被創建,只會有一個該靜態成員的副本。
關鍵字?static?意味著類中只有一個該成員的實例。靜態變量用于定義常量,因為它們的值可以通過直接調用類而不需要創建類的實例來獲取。靜態變量可在成員函數或類的定義外部進行初始化。你也可以在類的定義內部初始化靜態變量。
二、封裝
封裝是面向對象程序設計中最重要的概念之一。封裝?被定義為"把一個或多個項目封閉在一個物理的或者邏輯的包中"。在面向對象程序設計方法論中,封裝是為了防止對實現細節的訪問。
抽象和封裝是面向對象程序設計的相關特性。抽象允許相關信息可視化,封裝則使開發者實現所需級別的抽象。
C# 封裝根據具體的需要,設置使用者的訪問權限,并通過?訪問修飾符?來實現。
一個?訪問修飾符?定義了一個類成員的范圍和可見性。C# 支持的訪問修飾符如下所示:
1、public:所有對象都可以訪問;
2、private:對象本身在對象內部可以訪問;
3、protected:只有該類對象及其子類對象可以訪問
4、internal:同一個程序集的對象可以訪問;
5、protected internal:訪問限于當前程序集或派生自包含類的類型。
(一)、public
Public 訪問修飾符允許一個類將其成員變量和成員函數暴露給其他的函數和對象。任何公有成員可以被外部的類訪問。
代碼示例:


1 using System; 2 namespace RectangleApplication 3 { 4 class Rectangle 5 { 6 //成員變量 7 public double length; 8 public double width; 9 10 public double GetArea() 11 { 12 return length * width; 13 } 14 public void Display() 15 { 16 Console.WriteLine("長度: {0}", length); 17 Console.WriteLine("寬度: {0}", width); 18 Console.WriteLine("面積: {0}", GetArea()); 19 } 20 }// Rectangle 結束 21 22 class ExecuteRectangle 23 { 24 static void Main(string[] args) 25 { 26 Rectangle r = new Rectangle(); 27 r.length = 4.5; 28 r.width = 3.5; 29 r.Display(); 30 Console.ReadLine(); 31 } 32 } 33 } 34 //執行結果: 35 //長度: 4.5 36 //寬度: 3.5 37 //面積: 15.75
(二)、private
Private 訪問修飾符允許一個類將其成員變量和成員函數對其他的函數和對象進行隱藏。只有同一個類中的函數可以訪問它的私有成員。即使是類的實例也不能訪問它的私有成員。
代碼示例:


1 using System; 2 namespace RectangleApplication 3 { 4 class Rectangle 5 { 6 //成員變量 7 private double length; 8 private double width; 9 10 public void Acceptdetails() 11 { 12 Console.WriteLine("請輸入長度:"); 13 length = Convert.ToDouble(Console.ReadLine()); 14 Console.WriteLine("請輸入寬度:"); 15 width = Convert.ToDouble(Console.ReadLine()); 16 } 17 public double GetArea() 18 { 19 return length * width; 20 } 21 public void Display() 22 { 23 Console.WriteLine("長度: {0}", length); 24 Console.WriteLine("寬度: {0}", width); 25 Console.WriteLine("面積: {0}", GetArea()); 26 } 27 }//end class Rectangle 28 class ExecuteRectangle 29 { 30 static void Main(string[] args) 31 { 32 Rectangle r = new Rectangle(); 33 r.Acceptdetails(); 34 r.Display(); 35 Console.ReadLine(); 36 } 37 } 38 } 39 //執行結果: 40 //請輸入長度: 41 //4.4 42 //請輸入寬度: 43 //3.3 44 //長度: 4.4 45 //寬度: 3.3 46 //面積: 14.52
(三)、protected
Protected 訪問修飾符允許子類訪問它的基類的成員變量和成員函數。這樣有助于實現繼承。我們將在繼承的章節詳細討論這個。更詳細地討論這個。
(四)、internal
Internal 訪問說明符允許一個類將其成員變量和成員函數暴露給當前程序中的其他函數和對象。換句話說,帶有 internal 訪問修飾符的任何成員可以被定義在該成員所定義的應用程序內的任何類或方法訪問。
代碼示例:


1 using System; 2 3 namespace RectangleApplication 4 { 5 class Rectangle 6 { 7 //成員變量 8 internal double length; 9 internal double width; 10 11 double GetArea() 12 { 13 return length * width; 14 } 15 public void Display() 16 { 17 Console.WriteLine("長度: {0}", length); 18 Console.WriteLine("寬度: {0}", width); 19 Console.WriteLine("面積: {0}", GetArea()); 20 } 21 }//end class Rectangle 22 class ExecuteRectangle 23 { 24 static void Main(string[] args) 25 { 26 Rectangle r = new Rectangle(); 27 r.length = 4.5; 28 r.width = 3.5; 29 r.Display(); 30 Console.ReadLine(); 31 } 32 } 33 } 34 //執行結果: 35 //長度: 4.5 36 //寬度: 3.5 37 //面積: 15.75
(五)、protected internal
Protected Internal 訪問修飾符允許在本類,派生類或者包含該類的程序集中訪問。這也被用于實現繼承。
注:
-
-
- ?Pubilc?:任何公有成員可以被外部的類訪問。
- ?Private?:只有同一個類中的函數可以訪問它的私有成員。
- ?Protected?:該類內部和繼承類中可以訪問。
- ?internal?: 同一個程序集的對象可以訪問。
- ?Protected internal?:3 和 4 的并集,符合任意一條都可以訪問。
-
1、范圍比較:private < internal/protected < protected internal < public
2、public 和 internal 修飾符的區別:
一個是國際妓女,誰用都可以,就是 public,一個是不懂外語的,只能在國內做生意,就是 internal,只能在當前所在的工程里面引用。你建兩個工程,建立一個引用關系,被引用的里面建兩個類,就能區別了。用 VS 的話,你根本點不出來那個不懂外語的。
三、繼承
繼承是面向對象程序設計中最重要的概念之一。繼承允許我們根據一個類來定義另一個類,這使得創建和維護應用程序變得更容易。同時也有利于重用代碼和節省開發時間。
當創建一個類時,程序員不需要完全重新編寫新的數據成員和成員函數,只需要設計一個新的類,繼承了已有的類的成員即可。這個已有的類被稱為的基類,這個新的類被稱為派生類。
繼承的思想實現了?屬于(IS-A)?關系。例如,哺乳動物?屬于(IS-A)?動物,狗?屬于(IS-A)?哺乳動物,因此狗?屬于(IS-A)?動物。
基類與派生類:一個類可以派生自多個類或接口,這意味著它可以從多個基類或接口繼承數據和函數。 - 父類與子類。
(一)、基類的初始化
派生類繼承了基類的成員變量和成員方法。因此父類對象應在子類對象創建之前被創建。您可以在成員初始化列表中進行父類的初始化。
代碼示例:


1 using System; 2 namespace RectangleApplication 3 { 4 class Rectangle 5 { 6 // 成員變量 7 protected double length; 8 protected double width; 9 public Rectangle(double l, double w) 10 { 11 length = l; 12 width = w; 13 } 14 public double GetArea() 15 { 16 return length * width; 17 } 18 public void Display() 19 { 20 Console.WriteLine("長度: {0}", length); 21 Console.WriteLine("寬度: {0}", width); 22 Console.WriteLine("面積: {0}", GetArea()); 23 } 24 }//end class Rectangle 25 class Tabletop : Rectangle 26 { 27 private double cost; 28 public Tabletop(double l, double w) : base(l, w) 29 { } 30 public double GetCost() 31 { 32 double cost; 33 cost = GetArea() * 70; 34 return cost; 35 } 36 public void Display() 37 { 38 base.Display(); 39 Console.WriteLine("成本: {0}", GetCost()); 40 } 41 } 42 class ExecuteRectangle 43 { 44 static void Main(string[] args) 45 { 46 Tabletop t = new Tabletop(4.5, 7.5); 47 t.Display(); 48 Console.ReadLine(); 49 } 50 } 51 } 52 //執行結果 53 //長度: 4.5 54 //寬度: 7.5 55 //面積: 33.75 56 //成本: 2362.5
(二)、多重繼承
多重繼承指的是一個類別可以同時從多于一個父類繼承行為與特征的功能。與單一繼承相對,單一繼承指一個類別只可以繼承自一個父類。
C# 不支持多重繼承。但是,您可以使用接口來實現多重繼承。
代碼示例:


1 using System; 2 namespace InheritanceApplication 3 { 4 class Shape 5 { 6 public void setWidth(int w) 7 { 8 width = w; 9 } 10 public void setHeight(int h) 11 { 12 height = h; 13 } 14 protected int width; 15 protected int height; 16 } 17 18 // 基類 PaintCost 19 public interface PaintCost 20 { 21 int getCost(int area); 22 23 } 24 // 派生類 25 class Rectangle : Shape, PaintCost 26 { 27 public int getArea() 28 { 29 return (width * height); 30 } 31 public int getCost(int area) 32 { 33 return area * 70; 34 } 35 } 36 class RectangleTester 37 { 38 static void Main(string[] args) 39 { 40 Rectangle Rect = new Rectangle(); 41 int area; 42 Rect.setWidth(5); 43 Rect.setHeight(7); 44 area = Rect.getArea(); 45 // 打印對象的面積 46 Console.WriteLine("總面積: {0}", Rect.getArea()); 47 Console.WriteLine("油漆總成本: ${0}" , Rect.getCost(area)); 48 Console.ReadKey(); 49 } 50 } 51 } 52 //執行結果: 53 //總面積: 35 54 //油漆總成本: $2450
注:
為什么一個對象可以用父類聲明,卻用子類實例化?
這個實例是子類的,但是因為你聲明時是用父類聲明的,所以你用正常的辦法訪問不到子類自己的成員,只能訪問到從父類繼承來的成員。
在子類中用 override 重寫父類中用 virtual 申明的虛方法時,實例化父類調用該方法,執行時調用的是子類中重寫的方法;
如果子類中用 new 覆蓋父類中用 virtual 申明的虛方法時,實例化父類調用該方法,執行時調用的是父類中的虛方法;


1 /// <summary> 2 /// 父類 3 /// </summary> 4 public class ParentClass 5 { 6 public virtual void ParVirMethod() 7 { 8 Console.WriteLine("父類的方法..."); 9 } 10 } 11 12 /// <summary> 13 /// 子類1 14 /// </summary> 15 public class ChildClass1 : ParentClass 16 { 17 public override void ParVirMethod() 18 { 19 Console.WriteLine("子類1的方法..."); 20 } 21 } 22 23 /// <summary> 24 /// 子類2 25 /// </summary> 26 public class ChildClass2 : ParentClass 27 { 28 public new void ParVirMethod() 29 { 30 Console.WriteLine("子類2的方法..."); 31 } 32 33 public void Test() 34 { 35 Console.WriteLine("子類2的其他方法..."); 36 } 37 } 38 39 //執行調用: 40 ParentClass par = new ChildClass1(); 41 par.ParVirMethod(); //結果:"子類1的方法",調用子類的方法,實現了多態 42 43 par = new ChildClass2(); 44 par.ParVirMethod(); //結果:"父類的方法",調用父類的方法,沒有實現多態
深究其原因,為何兩者不同,是因為原理不同:?override是重寫,即將基類的方法在派生類里直接抹去重新寫,故而調用的方法就是子類方法;而new只是將基類的方法在派生類里隱藏起來,故而調用的仍舊是基類方法。
應用舉例:
有這樣的需要,比如 People 類有一個 Run 方法,Man 和 Woman 這兩個類都是繼承自 People 的類,并且都重寫(override)了 Run 這個方法(男人女人跑起步來不一樣)。
現在有一群人一起跑步,有男人有女人。
我們可以把這些都裝進一個People數組(假設為peoples)。
然后:
foreach(People p in peoples) // peoples中對象不同(即有男有女),用于實例化的子類就不同。 { p.Run(); // 故而,調用的方法也不同,實現了多態 }
由于多態性,在調用 p.Run() 的時候 p 對象本身如果是男人就會自動調用男人的 Run 方法,是女人就會調用女人的 Run 方法。
依賴倒置原則
依賴倒置原則,DIP,Dependency Inverse Principle DIP的表述是:
1、高層模塊不應該依賴于低層模塊, 二者都應該依賴于抽象。
2、抽象不應該依賴于細節,細節應該依賴于抽象。
這里說的“依賴”是使用的意思,如果你調用了一個類的一個方法,就是依賴這個類,如果你直接調用這個類的方法,就是依賴細節,細節就是具體的類,但如果你調用的是它父類或者接口的方法,就是依賴抽象, 所以 DIP 說白了就是不要直接使用具體的子類,而是用它的父類的引用去調用子類的方法,這樣就是依賴于抽象,不依賴具體。
其實簡單的說,DIP 的好處就是解除耦合,用了 DIP 之后,調用者就不知道被調用的代碼是什么,因為調用者拿到的是父類的引用,它不知道具體指向哪個子類的實例,更不知道要調用的方法具體是什么,所以,被調用代碼被偷偷換成另一個子類之后,調用者不需要做任何修改, 這就是解耦了。
四、多態
多態性意味著有多重形式。在面向對象編程范式中,多態性往往表現為"一個接口,多個功能"。
多態性可以是靜態的或動態的。在靜態多態性中,函數的響應是在編譯時發生的。在動態多態性中,函數的響應是在運行時發生的。
(一)、靜態多態性
在編譯時,函數和對象的連接機制被稱為早期綁定,也被稱為靜態綁定。C# 提供了兩種技術來實現靜態多態性。分別為:
-
-
- 函數重載
- 運算符重載
-
運算符重載將在下一章節討論,接下來我們將討論函數重載。
(二)、函數重載
您可以在同一個范圍內對相同的函數名有多個定義。函數的定義必須彼此不同,可以是參數列表中的參數類型不同,也可以是參數個數不同。不能重載只有返回類型不同的函數聲明。下面的實例演示了幾個相同的函數?print(),用于打印不同的數據類型:
代碼示例:


1 using System; 2 namespace PolymorphismApplication 3 { 4 class Printdata 5 { 6 void print(int i) 7 { 8 Console.WriteLine("Printing int: {0}", i ); 9 } 10 11 void print(double f) 12 { 13 Console.WriteLine("Printing float: {0}" , f); 14 } 15 16 void print(string s) 17 { 18 Console.WriteLine("Printing string: {0}", s); 19 } 20 static void Main(string[] args) 21 { 22 Printdata p = new Printdata(); 23 // 調用 print 來打印整數 24 p.print(5); 25 // 調用 print 來打印浮點數 26 p.print(500.263); 27 // 調用 print 來打印字符串 28 p.print("Hello C++"); 29 Console.ReadKey(); 30 } 31 } 32 } 33 //執行結果: 34 //Printing int: 5 35 //Printing float: 500.263 36 //Printing string: Hello C++
(三)、動態多態性
C# 允許您使用關鍵字?abstract?創建抽象類,用于提供接口的部分類的實現。當一個派生類繼承自該抽象類時,實現即完成。抽象類包含抽象方法,抽象方法可被派生類實現。派生類具有更專業的功能。
請注意,下面是有關抽象類的一些規則:
-
-
- 您不能創建一個抽象類的實例。
- 您不能在一個抽象類外部聲明一個抽象方法。
- 通過在類定義前面放置關鍵字?sealed,可以將類聲明為密封類。當一個類被聲明為?sealed?時,它不能被繼承。抽象類不能被聲明為 sealed。
-
代碼示例:


1 using System; 2 namespace PolymorphismApplication 3 { 4 abstract class Shape 5 { 6 abstract public int area(); 7 } 8 class Rectangle: Shape 9 { 10 private int length; 11 private int width; 12 public Rectangle( int a=0, int b=0) 13 { 14 length = a; 15 width = b; 16 } 17 public override int area () 18 { 19 Console.WriteLine("Rectangle 類的面積:"); 20 return (width * length); 21 } 22 } 23 24 class RectangleTester 25 { 26 static void Main(string[] args) 27 { 28 Rectangle r = new Rectangle(10, 7); 29 double a = r.area(); 30 Console.WriteLine("面積: {0}",a); 31 Console.ReadKey(); 32 } 33 } 34 } 35 //執行結果: 36 //Rectangle 類的面積: 37 //面積: 70
當有一個定義在類中的函數需要在繼承類中實現時,可以使用虛方法。虛方法是使用關鍵字?virtual?聲明的。虛方法可以在不同的繼承類中有不同的實現。對虛方法的調用是在運行時發生的。
動態多態性是通過?抽象類?和?虛方法?實現的。
代碼示例:


1 using System; 2 namespace PolymorphismApplication 3 { 4 class Shape 5 { 6 protected int width, height; 7 public Shape( int a=0, int b=0) 8 { 9 width = a; 10 height = b; 11 } 12 public virtual int area() 13 { 14 Console.WriteLine("父類的面積:"); 15 return 0; 16 } 17 } 18 class Rectangle: Shape 19 { 20 public Rectangle( int a=0, int b=0): base(a, b) 21 { 22 23 } 24 public override int area () 25 { 26 Console.WriteLine("Rectangle 類的面積:"); 27 return (width * height); 28 } 29 } 30 class Triangle: Shape 31 { 32 public Triangle(int a = 0, int b = 0): base(a, b) 33 { 34 35 } 36 public override int area() 37 { 38 Console.WriteLine("Triangle 類的面積:"); 39 return (width * height / 2); 40 } 41 } 42 class Caller 43 { 44 public void CallArea(Shape sh) 45 { 46 int a; 47 a = sh.area(); 48 Console.WriteLine("面積: {0}", a); 49 } 50 } 51 class Tester 52 { 53 54 static void Main(string[] args) 55 { 56 Caller c = new Caller(); 57 Rectangle r = new Rectangle(10, 7); 58 Triangle t = new Triangle(10, 5); 59 c.CallArea(r); 60 c.CallArea(t); 61 Console.ReadKey(); 62 } 63 } 64 } 65 //執行結果: 66 //Rectangle 類的面積: 67 //面積:70 68 //Triangle 類的面積: 69 //面積:25
注:
1、virtual和abstract都是用來修飾父類的,通過覆蓋父類的定義,讓子類重新定義。
-
-
- virtual修飾的方法必須有實現(哪怕是僅僅添加一對大括號),而abstract修飾的方法一定不能實現。
- virtual可以被子類重寫,而abstract必須被子類重寫。
- 如果類成員被abstract修飾,則該類前必須添加abstract,因為只有抽象類才可以有抽象方法。
- 無法創建abstract類的實例,只能被繼承無法實例化。
-
2、overload和override
重載(overload)是提供了一種機制, 相同函數名通過不同的返回值類型以及參數來表來區分的機制。
重寫(override)是用于重寫基類的虛方法,這樣在派生類中提供一個新的方法。
3、抽象方法和虛方法
-
-
- 虛方法必須有實現部分,抽象方法沒有提供實現部分,抽象方法是一種強制派生類覆蓋的方法,否則派生類將不能被實例化。
- ?抽象方法只能在抽象類中聲明,虛方法不是。如果類包含抽象方法,那么該類也是抽象的,也必須聲明類是抽象的。
- ?抽象方法必須在派生類中重寫,這一點和接口類似,虛方法不需要再派生類中重寫。
-
簡單說,抽象方法是需要子類去實現的。虛方法是已經實現了的,可以被子類覆蓋,也可以不覆蓋,取決于需求。抽象方法和虛方法都可以供派生類重寫。
對于面向對象的特性,推薦幾篇不錯的文章:
https://www.cnblogs.com/harrogath/p/6445793.html
https://www.cnblogs.com/autumn001/p/9036148.html
?五、下期預告-敬請期待
->運算符重載
->接口
->命名空間
->預處理器指令
->正則表達式
->異常處理
->文件的輸入輸出
參考文獻:https://www.runoob.com/csharp/csharp-encapsulation.html
?根據w3school自我溫習一下c#基礎,分享給大家。