文章目錄
- 7.1面向物件的基本概念
- 7.1.1 什么是面向物件編程
- 7.1.2 面向物件編程的特點
- 7.2 類
- 7.2.1 類的概念
- 7.2.2 類的宣告
- 7.2.3 類的成員
- 7.2.4 類成員訪問修飾符
- 7.3 物件
- 7.4 建構式和解構式
- 7.4.1 建構式
- 7.4.2 解構式
- 7.5 方法
- 7.5.1 方法的宣告
- 7.5.2 方法的引數
- 7.5.3 靜態和非靜態方法
- 7.5.4 方法的多載
- 7.6 欄位和屬性
- 7.6.1 欄位概念及用途
- 7.6.2 欄位的宣告
- 7.6.3 屬性的概念及用途
- 7.6.4 屬性的宣告及使用
- 7.7 繼承和多型
- 7.7.1 基類和派生類
- 7.7.2 多型
- 本章小結
7.1面向物件的基本概念
7.1.1 什么是面向物件編程
物件=(演算法+資料結構)
OOP程式=(物件+物件+……)
程式就是許多物件在計算機中相繼表現自己,而物件則是一個個程式物體,
7.1.2 面向物件編程的特點
1. 封裝性
封裝: 是指將資料成員、屬性、事件和方法(統稱為成員)集合在一個整體里的程序,
隱藏: 對內部細節隱藏保護的能力,類內的某些成員可以以對外隱藏的特性被保護起來,從而保證了類具有較好的獨立性,防止外部程式破壞類的內部資料,同時便于程式的維護和修改,
2. 繼承性
繼承: 利用現有類派生出新類的程序,
特點: 新類擁有原有類的特性,又增加了自身新的特性,設計程式時,只需對新增的內容或是對原內容的修改設計代碼,
作用: 繼承性可簡化類和物件的創建作業量,增強代碼的可重用性,
例如:有了人類,要定義學生類,只要增加一個學號屬性,
3. 多型性
多型性:不同的類進行同一操作可以有不同的行為,同樣的訊息被不同型別的物件接收時導致完全不同的行為,多型性允許每個物件以適合自身的方式去回應共同的訊息,不必為相同功能的操作作用于不同的物件而去特意識別,
例如,定義一個狗類和一個貓類,狗和貓都會叫(可定義方法bark),說明兩者在這方面可以進行相同的操作,然而,狗和貓叫的行為是截然不同的,因為狗叫是“汪汪”,而貓叫是“喵喵” ,
7.2 類
7.2.1 類的概念
類(class)是面向物件技術中最重要的一種資料結構,是指具有相同屬性和操作的一組物件的抽象集合,它支持資訊隱藏和封裝,進而支持對抽象資料型別(Abstract Data Type,ADT)的實作,
類包含:資料成員(常量和域)、函式成員(方法、屬性、事件、索引器、運算子、實體建構式、解構式和靜態建構式)和嵌套型別,
7.2.2 類的宣告
類的屬性集 類的修飾符 關鍵字class 類名 繼承方式 基類名
{
}
如:定義汽車類
public class Car
{
public string color;
public string brand;
}
C#中常用的類修飾符:
| 序號 | 修飾符 | 說明 |
|---|---|---|
| 1 | public | 表示不限制對該類的訪問 |
| 2 | protected | 表示只能對其所在類和所在類的子類進行訪問 |
| 3 | internal | 只有其所在類才能訪問,不允許外部程式集使用該類 |
| 4 | private | 只有.NET中的應用程式或庫才能訪問 |
| 5 | new | 僅允許在嵌套類宣告時使用,表明類中隱藏了由基類中繼承而來的、與基類中同名的成員 |
| 6 | abstract | 抽象類,不允許建立類的實體 |
| 7 | sealed | 密封類,不允許被繼承 |
7.2.3 類的成員
? 區域變數:在指定范圍內有效,
? 欄位:即類中的變數或常量,包括靜態欄位、實體欄位、常量和只讀欄位,
? 方法成員:包括靜態方法和實體方法,
? 屬性:按屬性指定的get方法和Set方法對欄位進行讀寫,屬性本質上是方法,
? 事件:代表事件本身,同時聯系事件和事件處理函式,
? 索引指示器:允許像使用陣列那樣訪問類中的資料成員,
? 運算子多載:采用多載運算子的方法定義類中特有的操作,
? 建構式和解構式,
包含可執行代碼的成員被認為是類中的函式成員,這些函式成員有方法、屬性、索引指示器、運算子多載、建構式和解構式,
7.2.4 類成員訪問修飾符
Private:私有資料成員只能被類內部的函式使用和修改,私有函式成員只能被類內部的函式呼叫,派生類雖然繼承了基類私有成員,但不能直接訪問它們,只能通過基類的公有成員訪問,
Protected:保護資料成員只能被類內部和派生類的函式使用和修改,保護函式成員只能被類內部和派生類的函式呼叫,
Public:類的公用函式成員可以被類的外部程式所呼叫,類的公用資料成員可以被類的外部程式直接使用,公有函式實際是一個類和外部通訊的介面,外部函式通過呼叫公有函式,按照預先設定好的方法修改類的私有成員和保護成員,
Internal:內部成員只能在同一程式集中的檔案中才是可以訪問的,一般是同一個應用(Application)或庫(Library),
7.3 物件
物件是類的實體,是OOP應用程式的一個組成部件,
汽車類是抽象的概念,它有顏色和品牌,是所有汽車的集合,是“類”;而一輛紅色奔馳車則是汽車類的一個具體實體,是“物件”,
變數型別是一個類,變數也是一個物件,
物件的定義、實體化及訪問
【例7-1】實作訪問Car類的物件和物件資料狀態,
public class Car
{
public int number; //號碼屬性
public string color; //顏色屬性
private string _brand; //品牌域
public Car()
{
}
public string brand //品牌屬性
{
get //讀操作
{
return this._brand;
}
set //寫操作
{
this._brand = value;
}
}
}
7.4 建構式和解構式
建構式和解構式是類中比較特殊的兩種成員函式,主要用來對物件進行初始化和回收物件資源,
建構式的名字和類名相同,解構式和建構式的名字相同,但解構式要在名字前加一個波浪號(~),
7.4.1 建構式
? 建構式的名稱與類名相同;
? 建構式不宣告回傳型別;
? 建構式通常是公有的(使用public訪問限制修飾符宣告),如果宣告為保護的(protected)或私有的(private),則該構造
函式不能用于類的實體化;
? 建構式的代碼中通常只進行物件初始化作業,而不應執行其他操作;
? 建構式在創建物件時被自動呼叫,不能像其他方法那樣顯式地呼叫建構式,
【例7-2】使用建構式對類Person的3個欄位進行初始化,
public class Person
{
public string m_name;
protected int m_age;
protected bool m_gender;
//建構式
public Person()
{
m_name = "Unknown";
m_age = 0;
m_gender = false;
}
}
一個類定義必須且至少有一個建構式,如果定義類時,沒有宣告建構式,系統會提供一個默認的建構式,不帶任何引數的建構式稱為默認建構式,如果宣告了建構式,系統將不再提供默認建構式,
為了創建實體的方便,一個類可以有多個具有不同引數串列的建構式,即建構式可以多載,
public class Xyz {
// 成員變數
int x;
public Xyz() { //引數表為空的建構式
x = 0; // 默認創建物件
}
public Xyz(int i) { //帶一個引數的建構式
x = i; // 使用引數創建物件
}
}
7.4.2 解構式
解構式不接受任何引數,也不回傳任何值;
解構式不能使用任何訪問限制修飾符;
解構式的代碼中通常只進行銷毀物件的作業,而不應執行其他的操作;
解構式不能被繼承,也不能被顯式地呼叫,
public class Person
{
private string m_name;
private int m_age;
private bool m_gender;
//解構式
~Person()
{
}
}
7.5 方法
7.5.1 方法的宣告
方法是一種用于實作可以由物件或類執行的計算或操作的成員,類的方法主要是和類相關聯的動作,它是類的外部界面,對于那些私有的欄位來說,外部界面實作對它們的操作一般只能通過方法來實作,
語法格式:
訪問修飾符 回傳型別 方法名(引數串列){ }
? 方法的訪問修飾符通常是public,以保證在類定義外部能夠呼叫該方法,
? 方法的回傳型別用于指定由該方法計算和回傳的值的型別,可以是任何值型別或參考型別資料,如,int、string及前面定義的Person類,如果方法不回傳一個值,則它的回傳型別為void,
? 方法名是一個合法的C#識別符號,
? 引數串列在一對圓括號中,指定呼叫該方法時需要使用的引數個數、各個引數的型別,引數之間以逗號分隔,
? 實作特定功能的陳述句塊放在一對大括號中,叫方法體,“{”表示方法體的開始,“}”表示方法體的結束,
? 如果方法有回傳值,則方法體中必須包含一個return陳述句,以指定回傳值,其型別必須和方法的回傳型別相同,如果方法無回傳值,在方法體中可以不包含return陳述句,
例如:無回傳值的方法method:
public void method()
{
Console.Write("方法宣告");
}
7.5.2 方法的引數
? 在方法宣告中使用的引數叫形式引數(形參)
? 在呼叫方法中使用的引數叫實際引數(實參)
? 在呼叫方法時,引數傳遞就是將實參傳遞給形參的程序,
例如,某類定義中宣告方法時的形參如下:
public int IntMax(int a,int b){}
則宣告物件classmax后呼叫方法時的實參為:
classmax.IntMax(x,y)
? 呼叫方法時,傳遞給方法的引數型別應該與方法定義的引數型別相同,或者是能夠隱式轉換為方法定義的引數型別,
? 如果方法進行處理和更改數值等操作,有時需要傳遞引數值給方法并從方法獲得回傳值,下面是引數值的4種常用情況,
- 值引數
- 參考引數
- 輸出引數
- 引數陣列
值引數
宣告時不帶修飾符的引數是值引數,一個值引數相當于一個區域變數,初始值來自該方法呼叫時提供的相應的實參,引數按值的方式傳遞是指當把實參傳遞給形參時,是把實參的值復制(拷貝)給形參,實參和形參使用的是兩個不同記憶體中的值,所以這種引數傳遞方式的特點是形參的值發生改變時,不會影響實參的值,從而保證了實參資料的安全, 之所以叫做值型別,是因為傳遞的是物件的副本而不是物件本身,傳遞的是值,而不是同一個物件,
【例7-3】值引數傳遞,
CzMath c = new CzMath();
c.Swap(a, b);
……
class CzMath
{ //交換兩個數的值
public void Swap(double x, double y)
{ double temp = x;
x = y;
y = temp;
}
}
程式中,方法Swap試圖通過一個臨時變數來交換a和b的值,但程式運行后的結果仍然是:
a = 100;
b = 150;
這是因為呼叫Swap方法時,首先將主方法Main中定義的變數a和b(實參)進行一次復制,而后傳遞給方法的形參,在方法的執行代碼中所改變的只是復制的值,而不是實際引數的原始值,因此達不到交換數值的效果,
上面傳遞引數的方法叫做值傳遞值傳遞:
實參的值復制(拷貝)給形參,實參和形參使用的是兩個不同記憶體中的值,所以這種引數傳遞方式的特點是形參的值發生改變時,不會影響實參的值,從而保證了實參資料的安全,
如果希望改變實參的值,則需要另一種傳遞引數的方法參考傳遞,
參考引數
參考傳遞是指實參傳遞給形參時,不是將實參的值復制給形參,而是將實參的參考傳遞給形參,實參與形參使用的是一個記憶體中的值,這種引數傳遞方式的特點是形參的值發生改變時,同時也改變實參的值,
參考引數使用ref修飾符,告訴編譯器,實參與形參的傳遞方式是參考,
【例7-4】參考引數傳遞,
……
CzMath c = new CzMath();
c.Swap(ref a, b);
……
class CzMath
{ //交換兩個數的值
public void Swap(ref double x, double y)
{ double temp = x;
x = y;
y = temp;
}
}
類物件引數總是按參考傳遞的,所以類物件引數傳遞不需要使用ref關鍵字,
程式中,傳遞給方法的引數就不是實參的拷貝,而是指向實參的參考,所以當對形參進行修改時,相應的實參也發生了改變,程式運行的結果表明了數值交換的成功:
a = 150;
b = 100;
輸出引數
在傳遞的引數前加out關鍵字,即可將該傳遞引數設定為一個輸出引數,
與參考引數類似,輸出引數也不開辟新的記憶體區域,它與參考型引數的差別在于,呼叫方法前無需對變數進行初始化,輸出型引數用于傳遞方法回傳的資料,
out修飾符后應跟隨與形參的型別相同的型別宣告,在方法回傳后,傳遞的變數被認為經過了初始化,
【例7-5】輸出引數傳遞,
……
double ave; //ave未初始化
CzMath m = new CzMath();
m.Average(a, b, out ave);
……
class CzMath
{
public void Average(int x, int y, out double z)
{
z = Convert.ToDouble(x + y) / 2;
}
}
引數陣列
引數陣列必須用params修飾詞明確指定,
方法的引數型別也可以是陣列 ,
public double Average(int[] array)
{ double ave = 0;
int sum = 0;
for(int i = 0; i < array.Length; i++)
sum += array[i];
ave = (double)sum / array.Length;
return ave;
}
public double Average(params int[] array)
{……}
呼叫Average方法的示例代碼:
int[] a = { 1, 3, 3, 5, 7, 9 };
int x = Average(a);
int y = Average(1, 3, 5, 7, 9);
int z = Average(10, 20, 30);
如果在形參前面加上關鍵字params,該形參就成為了引數陣列,
public double Average(params int[] array)
{ double ave = 0;
int sum = 0;
for(int i = 0; i < array.Length; i++)
sum += array[i];
ave = (double)sum / array.Length;
return ave;
}
傳遞給陣列型引數的實參既可以是一個陣列,也可以是任意多個陣列元素型別的變數,
例如,呼叫Average的代碼:
int[] a = { 1, 3, 3, 5, 7, 9 };
int x = Average(a);
int y = Average(1, 3, 5, 7, 9);
int z = Average(10, 20, 30);
都是合法的 ,
C#中對于引數陣列有著嚴格的規定:
- 1 在方法的引數串列中只允許出現一個引數陣列,而且在方法同時具有固定引數和引數陣列的情況下,引數陣列必須放在整個引數串列的最后,
- 2 引數陣列只允許是一維陣列,
- 3 引數陣列不能同時作為參考引數或輸出引數,
【例7-6 】引數陣列的使用-計算陣列所有元素的平均值 ,
7.5.3 靜態和非靜態方法
方法分為靜態方法(方法宣告中含有static修飾符 )和非靜態方法(方法宣告中沒有static修飾符 ),
1.靜態方法
(1)靜態方法不對特定實體進行操作,在靜態方法中參考this會導致編譯錯誤,
(2)C#中通過關鍵字static來定義靜態成員,和實體成員不同,使用靜態成員時,圓點連接符的前面不再是某個具體的物件變數,而是類的名稱,如Console.WriteLine和Console.ReadLine等方法就屬于靜態方法,使用時沒有創建哪個具體的Console類的實體,而是直接使用類本身,
例7-4類CzMath的Swap方法只用于交換兩個數值,而與具體的物件無關,因此可以將其定義為靜態方法,這樣在呼叫方法時就無需創建CzMath類的實體,
【例7-7 】靜態方法,
……
// CzMath c = new CzMath();
//c.Swap(ref a, b);
CzMath.Swap(ref a, ref b);
……
class CzMath
{ //交換兩個數的值
public static void Swap(ref double x, double y)
{ double temp = x; x = y; y = temp; }
}
方法分為靜態方法(方法宣告中含有static修飾符 )和非靜態方法(方法宣告中沒有static修飾符 ),
2.非靜態方法
非靜態方法是對類的某個給定的實體進行操作,而且可以用this來訪問該方法,
例如程式例7-4中的
CzMath c = new CzMath();
c.Swap(ref a, ref b);
就是先實體化CzMath的物件c,然后用物件c去呼叫非靜態方法Swap(),
7.5.4 方法的多載
方法多載是指呼叫同一方法名,但各方法中引數的資料型別、個數或順序不同,類中有兩個以上的同名方法,但使用的引數型別、個數或順序不同,呼叫時,編譯器就可以判斷在哪種情況下呼叫哪種方法,
public int MethodTest(int i, int j)
{……}
public int MethodTest(int i)
{……}
public string MethodTest(string s)
{……}
7.6 欄位和屬性
為了保存類的實體的各種資料資訊,C#提供了兩種方法—欄位(或稱域、成員變數)和屬性,
屬性不是欄位,本質上是定義修改欄位的方法, 兩者密切相關,
7.6.1 欄位概念及用途
欄位也叫成員變數,表示存盤位置,用來保存類的各種資料資訊,
欄位是 C#中不可缺少的一部分,代表一個與物件或類相關的變數或常量,
一個欄位宣告可以把一個或多個給定型別的欄位引入,
欄位的宣告非常簡單,例如:
private int a;
7.6.2 欄位的宣告
欄位又可分為靜態欄位、實體欄位、常量和只讀欄位,
例如:
private static int myvar;
private int myvar;
private const int myvar=5;
private readonly int myvar=6;
【例7-8】四種欄位的使用,
7.6.3 屬性的概念及用途
屬性不是欄位,但必然和類中的某個或某些欄位相聯系,
屬性定義了得到和修改相聯系的欄位的方法,
C#中的屬性更充分地體現了物件的封裝性:不直接操作類的資料內容,而是通過訪問器進行訪問,借助于get和set方法對屬性的值進行讀寫,
當讀取屬性時,執行get訪問器的代碼塊;當向屬性分配一個新值時,執行set訪問器的代碼塊,
7.6.4 屬性的宣告及使用
屬性在類模塊內是通過以下方式宣告的:指定欄位的訪問級別,后面是屬性的型別,接下來是屬性的名稱,然后是宣告get訪問器和/或set訪問器的代碼模塊,
例如,在Student類中可以Name屬性來封裝對私有欄位name的訪問:
public class Student
{
private string name; //欄位
public string Name //屬性
{
get { return name; }
set { name = value; }
}
}
屬性
作為類的特殊函式成員,get和set訪問函式需要包含在屬性宣告的內部,而函式宣告只需要寫出get和set關鍵字即可,其中get訪問函式沒有引數,默認回傳型別就是屬性的型別,表示屬性回傳值;set訪問函式的默認回傳型別為void,且隱含了一個與屬性型別相同的引數value,表示要傳遞給屬性的值,
通過屬性來訪問隱藏的欄位,例如:
Student s1 = new Student(“李明”);
Console.WriteLine(s1.Name); //呼叫get訪問函式訪問name欄位
Console.WriteLine(“請輸入新姓名:”);
s1.Name = Console.ReadLine(); //呼叫set訪問函式修改name欄位
屬性可以只包含一個訪問函式,如只有get訪問函式,那么屬性的值不能被修改;如只有set訪問函式,則表明屬性的值只能寫不能讀,
例如,希望Student物件在創建之后就不允許修改其姓名,那么Name屬性的定義可以修改為:
public string Name //只讀屬性
{
get { return name; }
}
屬性的典型用法是一個共有屬性對應封裝一個私有或保護欄位,但這并非強制要求,屬性本質上是方法,在其代碼中可以進行各種控制和計算,
例如,在學生類中有個表示出生年份的私有欄位birthYear,那么表示年齡的屬性Age的回傳值應該是當前年份減去出生年份:
privage int birthYear;
public int Age
{
get{ return DateTime.Now.Year – birthYear;}
}
注意:在C# 3.0中,提供了名為“自動屬性”特征,它允許只寫出屬性及其訪問函式的名稱,編譯就會自動生成所要封裝的欄位以及訪問函式的執行代碼,
例如,
public class Student
{
public string Name { get;set;}
}
其效果和下面傳統的定義方式是一樣的:
public class Student
{
private string name; //欄位
public string Name //屬性
{
get { return name; }
set { name = value; }
}
}
7.7 繼承和多型
封裝、繼承和多型性是面向物件程式設計的3個基本要素
通過繼承,派生類能夠在增加新功能的同時,吸收現有類的資料和行為,從而提高軟體的可重用性,
而多型性使得程式能夠以統一的方式來處理基類和派生類的物件行為,甚至是未來的派生類,從而提高系統的可擴展性和可維護性,
7.7.1 基類和派生類
1.基類和派生類
所謂繼承,是指在已有類的基礎上構造新的類,新類繼承了原有類的資料成員、屬性、方法和事件,原有的類稱為基類,新類稱為派生類,
【例7-9】基類和派生類的例子,
為避免層次結構過于復雜,C#中的類不支持多繼承,即不允許一個派生類繼承多個基類,只有在類和介面之間可以實作多繼承,
.NET 類別庫本身在構造程序中就充分利用了繼承技術,System.Object類是其他所有類的基類,不僅如此,C#是完全面向物件的編程語言,其中所有的資料型別都是從類中衍生而來,因此 System.Object類也是其他所有資料型別的基類,
訪問基類成員
2.訪問基類成員
訪問基類成員涉及隱藏基類成員和base關鍵字的使用,
(1)隱藏基類成員
大多數情況下,派生類不會一成不變地繼承基類中的所有成員,如可能希望在某些欄位中存盤不同的資訊、在某些方法中執行不同的操作等,這時就需要通過new關鍵字來隱藏基類中的成員,
例如例7-9中,可能會希望StudentLeader類的NewPrintInfo()方法輸出不同的內容(除了基類的三個欄位之外,還需要輸出Duty),這時就可以將NewPrintInfo()方法定義為:
public new void PrintInfo()
{
Console.WriteLine(No);
Console.WriteLine(Name);
Console.WriteLine(Score);
Console.WriteLine(Duty);
}
提示:隱藏基類成員時所使用的new
關鍵字屬于一種修飾符,它和創建
物件時使用的new運算子是完全不同的,
這時稱派生類中重新定義的方法覆寫了基類中的同名方法,其中,new關鍵字放在訪問限制修飾符的前后均可,但一定要在成員的型別說明之前,
(2) base關鍵字的使用
base關鍵字用來訪問當前物件的基類物件,進而呼叫基類物件的成員,
例如,可以StudentLeader類的PrintInfo方法改寫成:
public new void PrintInfo()
{
base.PrintInfo();
Console.WriteLine(Duty);
}
這樣,派生類StudentLeader的PrintInfo()方法就呼叫了其隱藏的基類方法,
base的另外一個用途是在派生類中呼叫基類建構式,例如,上例中StudentLeader類的建構式可以改寫為:
public StudentLeader(string a, string b, int c,string d):base(a, b, c)
{
Duty = d;
}
其中,base(a, b, c)表示呼叫基類建構式Student(a,b,c),
7.7.2 多型
“多型性” 的含義:
同一事物在不同的條件下可以表現出不同的形態,
在物件之間進行通信時非常有用,如一個物件發送訊息到其他物件,它并不一定要知道接收訊息的物件屬于哪一類,接收到訊息之后,不同型別的物件可以做出不同的解釋,執行不同的操作,從而產生不同的結果,
1.虛擬方法和重寫方法
2.抽象類和抽象方法
3.密封類和密封方法
虛擬方法和重寫方法
派生類很少一成不變地去繼承基類中的所有成員,
? 一種情況是派生類中的方法成員可以隱藏基類中同名的方法成員,這時通過關鍵字new對成員加 以修飾;
? 另一種更為普遍和靈活的情況是將基類的方法成員定義為虛擬方法,而在派生類中對虛擬方法進行重寫,
? 后者的優勢在于它可以實作運行時的多型性,即程式可以在運行程序中確定應該呼叫哪一個方法成員,
基類的虛擬方法通過關鍵字virtual進行定義,而派生類的重寫方法則通過關鍵字override進行定義,
【例7-10】修改例7-9,要求使用虛擬方法和重寫方法,
說明:
1.如果在派生類中使用override關鍵字定義了重寫方法,那么也就允許該類自己的派生類繼續重寫這個方法,因此重寫方法默認也是一種虛 擬方法,但不能同時使用virtual和override修飾一個方法,
2.虛擬方法不能是私有的,而且在基類和派生類中,對同一個虛擬方法和重寫方法的訪問限制應當相同,即要么都使用public修飾符,要么都使用 protected修飾符,
多載和重寫
多載和重寫的區別:
- 重寫是派生類具有和基類同名的方法,即用派生類同名(可以同形參和回傳值)重寫基類的同名方法,所以也稱之為覆寫,屬于運行時多型;
- 多載是同一個類中具有同名的方法,其形參串列不能夠相同,屬于編譯時多型,
2.抽象類和抽象方法
基類中的虛擬方法允許在派生類中進行重寫,并在呼叫時動態地決定是執行基類的方法代碼,還是執行哪一個派生類的方法代碼,
C#中還可以為基類定義抽象方法,它強制性地要求所有派生類必須重寫該方法,
抽象方法使用關鍵字abstract定義,并且不提供方法的執行體,
例如:
//抽象方法
public abstract void Area();
包含抽象方法的類必須是抽象類,它也需要使用關鍵字abstract加以定義,
例如:
public abstract class Shape
{
//類的成員定義...
}
抽象類表達的是抽象的概念,它本身不與具體的物件相聯系,其作用是為派生類提供一個公共的界面,例如“圖形”就可以是一個抽象類,因為每一個具體的圖形物件必然是其派生類的實體,如“四邊形”物件、“圓形”物件等;但“四邊形”不是一個抽象類,因為“四邊形”物件既可以是其派生的“平行四邊形”、“正方 形”等特殊四邊形物件,也可以是一般的四邊形物件,
對于抽象類,不允許創建類的實體, 例如在定義了抽象類Shape之后,下面的代碼是錯誤的:
Shpae s = new Shape();
抽象類之間也可以進行繼承,抽象類要求其所有派生類都繼承它的抽象方法;而如果派生類不是抽象類,它就必須重寫這些抽象方法并提供實作代碼,
和虛擬方法類 似,派生類中對抽象方法的重寫也通過override關鍵字進行,抽象方法不能是私有的,而且抽象方法及其重寫方法的訪問限制應當相同,最后,抽象方法不 能同時使用virtual關鍵字進行修飾,
【例7-11】抽象類和抽象方法,
public abstract class Shape
{ //定義Shape抽象類
public const double PI = 3.14;
public abstract double area(); //抽象方法,不需要定義處理
}
3.密封類和密封方法
抽象類本身無法創建實體,而強制要求通過派生類實作功能,與之相反的是,在C#中還可以定義一種密封類,它不允許從中派生出其他的類,密封類通常位于類的繼承層次的最低層,或者是在其他一些不希望類被繼承的情況下使用,
密封類使用關鍵字sealed定義,例如:
public sealed class Circle : Shape
{
//類的成員定義...
}
4.說明
(1)有趣的是, 盡管密封類和抽象類是截然相反的兩個概念,但它們并不沖突:一個類可以同時被定義為密封類和抽象類,這意味著該類既不能被繼承、也不能被實體化,這只出現在一種情況之下,那就是該類的所有成員均為靜態成員,Console類就是這樣的一個類,
(2)類似的,如果方法在定義中使用了關鍵字sealed,它就成為密封方法,與密封類的不同之處在于密封類是指不允許有派生類的類,而密封方法則是指不允許被重寫的方法,密封方法所在的類不一定是密封類(這一點與抽象方法不同),而如果該類存在派生類,那么在派生類中就必須原封不動地繼承這個密封方法,此外, 密封方法本身也要求是一個重寫方法(即sealed和override必須在方法定義中同時出現)
例如,可在Rectangle類中將重寫 area()為密封的:
public sealed override double area()
{
return (width * height);
}
這樣在Rectangle類的所有派生類中,就不允許重寫該方法的實作,如果要在派生類中定義同名的方法,就必須使用關鍵字new來隱藏基類的方法,
public class square : rectangle
{
public new double area()
{
return (width * width);
}
}
本章小結
1.類是面向物件程式設計的基本單元,是C#中最重要的一種資料結構,這種資料結構可以包含欄位成員、方法成員以及其他的嵌套型別,
2.建構式、解構式、屬性、索引指示器、事件和運算子都可以視為方法成員,類的建構式用于物件的初始化,而解構式用于物件的銷毀,
3.利用屬性提供的訪問方法,可以隱藏資料處理的細節,更好地實作物件的封裝性,
4.繼承是面向物件的程式設計方法中實作可重用性的關鍵技術,
5.同一操作作用于不同的物件,可以有不同的解釋,產生不同的執行結果,這就是多型性,多型性通過派生類多載基類中的虛擬方法來實作,
6.C#中還提供了抽象和密封的概念,給繼承和多型性的實作帶來了更大的靈活性,抽象類和介面都把方法成員交給派生類去實作,而密封類不允許被繼承,密封方法不允許被多載,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/281678.html
標籤:其他
上一篇:C++ 中的繼承
下一篇:第三講:工業網路——物理介質
