第4章 面向對象程序員
• 到目前止,主要學習了C# 2008的基本語法:如何聲明變量,如何控制流程等。C# 2008是一個徹底面向對象的語言,要寫出語法正確、設計合理的好代碼,必須掌握面向對象的特性。從本章開始,咱們將邁進面向對象的大門,即面向對象的思想將取代面向過程的思想。
4.1 類
• C# 2008秉承了C++面向對象的全部關鍵概念:封裝、繼承和多態性。其類型模型是創建在.NET虛擬對象系統之上的。類是面向對象的程序設計的基本構成模塊。從定義上講,類是一種數據結構,這種數據結構能夠包含數據成員、函數成員等類型。其中數據成員類型有常量和事件;函數成員類型有方法、屬性和索引器等。
4.1.1 面向對象的概念
• 隨着計算機的應用愈來愈普遍,社會對軟件開發提出了更高的要求。然而軟件技術的進步卻遠遠落後於硬件技術的進步,人們經常沒法控制軟件開發的週期和成本,軟件質量老是沒法讓人滿意,即所謂的軟件危機。
• 爲了擺脫軟件危機,必須按照工程化的原則和方法來組織軟件開發工做。在涉及大量計算的問題上,面向過程的設計方法暴露了愈來愈多的不足。例如:
• 功能與數據分離,不符合對現實世界的認識。
• 基於模塊的設計方式,致使軟件修改困難。
• 自頂向下的設計方法,限制了軟件的可重用性,下降了開發效率,也致使最後開發出來的系統難以維護。
4.1.2 定義一個類
• 類是C#中的兩種基本封裝結構之一(另外一個是結構)。每一個可執行語句必須放在類或結構中。簡單的說,類是一種抽象的數據類型,可是抽象的程度可能不一樣。而對象就是一個類的實例。即類是對象的藍圖。
• 1.定義類
• 建立一個用戶定義的類很是簡單。的聲明格式以下:
• attributes class-modifiers class identifier class-base
• {
• data members //數據成員
• function //函數
• nested types //嵌套類型
• }
• 2.類的修飾符
• 類的修飾符能夠是如下幾種之一或者是它們的組合在類的聲明中同一修飾符不容許出現屢次。類的修飾符有abstract、internal、new、private、protected、public或sealed關鍵字。其中internal、private、protected和public關鍵字定義了對類的訪問。在類中聲明的類只能有public和internal訪問屬性。嵌套類能夠具備以上4種訪問類型中的一種,還能夠有protected internal訪問類型,它等效於「受保護的或內部的」訪問。其餘關鍵字涉及成員隱藏和類是否可以被實例化或繼承。
4.1.3 類成員的修飾符
• 類的成員能夠分爲兩大類:類自己所聲明的,以及從基類中繼承而來的。在編寫程序時,能夠對類的成員使用不一樣的訪問修飾符從而定義它們的訪問級別。類的成員有如下類型,如表所示。
4.1.4 類的構造函數
• 【本節示例參考:\示例代碼\Chap04\UseRectangular】
• 構造函數用於執行類的實例的初始化。每一個類都有構造函數,即便咱們沒有聲明它,編譯器也會自動地爲咱們提供一個默認的構造函數。在訪問一個類的時候,系統將最早執行構造函數中的語句。實際上,任何構造函數的執行都隱式地調用了系統提供默認的構造函數base()。
• 1.構造函數的概念
• 2.構造函數的訪問類型
4.1.5 類的析構函數
• 【本節示例參考:\示例代碼\Chap04\Destructor】
• 在類的實例超出範圍時,要確保其所佔的存儲能被回收,因而就出現了析構函數。在C++中,其是用來釋放內存並執行清除操做的。C#使用一個垃圾收集器來自動完成大部分的相似工做。析構函數定義方式以下:
• class Myclass
• {
• ~ Myclass() //類Myclass的析構函數
• {
• Perform cleanup operations
• }
• }
4.1.6 結構與類的區別
• 在上一章中已經介紹過結構。下面讓咱們看看結構和類的區別。
• 1.實例在內存中的位置以及內存回收
• 2.結構不支持繼承
• 3.結構的默認值
• 4.關鍵字this的含義
4.1.7 類的繼承
• 【本節示例參考:\示例代碼\Chap04\TestInheritance】
• 若是全部的類都處在同一級別上,這中沒有相互關係的平坦結構就會限制了系統面向對象的特性。繼承的引入,就是在類之間創建一種相交關係,使新定義的派生類的實例能夠繼承已有的基類的特徵和能力並且能夠加入新的特性或者是修改已有的特性,創建起類的層次。在C#中,定義類的繼承的語法格式以下所示:
• class subclass :baseclass
• {
• //類的成員和方法
• }
4.2 接口
• 接口是面向對象編程技術的一個重要內容。在代碼中,接口負責功能的定義;在項目中,接口負責類和結構的規範。同時其能聲明屬性、索引器、事件和方法的編程構造,不爲這些成員提供實現,只提供定義。雖然,一個類只能繼承一個基類,但它卻能夠實現任意數量的接口。結構也能實現接口。接口自己能夠從多個基接口派生。
4.2.1 接口的基本概念
• 【本節示例參考:\示例代碼\Chap04\TestInterface】
• 接口是類或結構的藍圖,即主要是對類或結構的構成進行限制,而具體的實現仍是由類或結構來具體實現的。
• 1.接口聲明
• 接口的基本格式爲:
• Interface-modifiers interface interface-name
• {
• Interface members //接口成員
• }
• 2.接口成員的定義
4.2.2 接口的繼承
• 相似於類的繼承性,接口也能夠繼承。接口繼承的語法和類繼承的語法相同。支持多繼承表如今一個接口能夠有多個基接口。基本格式:
• public Interface son-interface:father-interface,mother-inferface
• {
• Son-interface member //son-interface 接口的成員
• }
• 在上述定義中,接口要繼承接口要用到冒號和接口名,若是繼承多個接口,接口名先後列出,中間用逗號分割。接口son-interface繼承了father-interface和mother-inferface接口。實現son-interface接口的類型必須實如今son-interface、father-interface和mother-interface接口中聲明的成員。
4.2.3 接口的特色
• 接口具備本身獨特的特色:
• 接口能夠用任何可訪問性來聲明,可是接口成員必須全都是公共可訪問性。即接口成員不能有任何訪問修飾符。
• 不能使用static、virtual、abstract和sealed來定義。
• 接口沒有構造函數。
• 接口中不容許定義字段。
4.3 屬性和域
• 爲了保存類的實例的各類數據信息,C#爲咱們提供了兩種方法——屬性和域。屬性實現了良好的數據封裝和數據隱蔽。
4.3.1 域(field)
• 域表示與對象或類相關聯的變量,它使類和結構能夠封裝數據。定義須要知足類的須要,並選用合適的數據類型。在有些資料和文檔中也將其翻譯爲域。聲明格式以下:
• attributes field-modifiers type field-name = value; //聲明和初始化值類型
• attributes field-modifiers type field-name = new constructor_call //聲明和初始化值引用類型或者結構
4.3.2 靜態域和非靜態域
• 靜態域的聲明是使用static修飾符,其餘的域都是非靜態域。靜態域和非靜態域分
• 別屬於C#中靜態變量和非靜態變量。
• 若將一個域說明爲靜態的,不管創建多少個該類的實例,內存中只存在一個靜態數據的複製。當這個類的第一個實例創建時,域被初始化之後再進行類的實例化時,再也不進行初始化,全部屬於這個類的實例共享一個副本。
• 與之相反,非靜態域在類的每次實例化時,每一個實例都擁有一份單獨的複製。代碼演示了靜態域和非靜態域的區別。
• 【本示例參考:\示例代碼\Chap04\TestField2】
4.3.3 只讀域
• 域的聲明中若是加上了readonly 修飾符,代表該域爲只讀域。對於只讀域咱們只能在域的定義中和它所屬類的構造函數中進行修改,在其餘狀況下,域是「只讀」的。
• 熟悉C和C++程序員可能習慣了使用const和#define定義一些容易記住的名字來表示某個數值static和readonly修飾符能夠起到一樣的效果:
• public class A
• {
• public static readonly double PI = 3.14159;
• public static readonly Color White = new Color(255, 255, 255);
• public static readonly int kByte = 1024;
• //other members
• }
4.3.4 域的初始化
• 在C和C++中,未經初始化的變量是不能使用的。在C#中,系統將爲每一個未經初始化的變量提供一個默認值。這雖然在某種程度上保證了程序的安全性,但對本應初始化爲某個特殊值的變量忘記了初始化,也經常會致使程序的執行誤入歧途。
• 對於靜態變量、非靜態的對象變量和數組元素,這些變量自動初始化爲自己的默認值。對於全部引用類型的變量默認值爲null,全部值類型的變量的默認值如表所示。
4.3.5 屬性(property)
• 屬性是對現實世界中實體特徵的抽象,它提供了對類或對象性質的訪問。例如一個用戶的姓名、一個文件的大小或一個窗口的標題,均可以做爲屬性。類的屬性所描述的是狀態信息,在類的某個實例中屬性的值表示該對象的狀態值。
• 在屬性中提供了get和set訪問器來對屬性值進行讀寫,這就避免了直接操做屬性值,充分體現了面向對象中的封裝性。
• 屬性能夠是隻讀的或者可讀寫。咱們能夠像訪問或改變公有域那樣,訪問或改變屬性的值。能夠利用屬性來容許從外部訪問私有域的值。
4.3.6 訪問屬性的值
• 在屬性的訪問聲明中,對屬性的值的讀操做用get關鍵字標出,對屬性的值的寫操做用set關鍵字標出。除了使用abstract修飾符的抽象屬性,其餘屬性中get訪問器都經過return來讀取屬性的值,set訪問器都經過value來設置屬性的值。
• 【本示例參考:\示例代碼\Chap04\TestAttribute2】
4.4 索引器
• 索引器跟屬性同樣,都是爲了以更直觀的方式使用類。索引器在語法上比較簡單,容許像訪問數組那樣來訪問對象,即經過索引方式方便的訪問類的數據信息的方法。
4.4.1 索引器的基本概念
• 【本節示例參考:\示例代碼\Chap04\TestIndex1】
• 索引器聲明的方式跟屬性聲明的方式很像。基本格式以下:
• Indexer-modifiers this parameter-list
• {
• Get
• {
• body of get accessor //對索引部分的讀
• }
• Set
• {
• body of set accessor //對索引部分的寫
• }
• }
4.4.2 索引器的使用
• 【本節示例參考:\示例代碼\Chap04\TestIndex2】
• 在索引器中,若是隻定義了一個get存取器,則索引器是隻讀的;若是隻定義了一個set存取器,索引器能夠是隻寫的;若是同時都定義了,則索引器就是可讀寫的。[]運算符用來調用索引器的get或set存取器,放在「[]」中參數必須對應索引器定義的參數列表,同時「[]」運算符的前面要加上類或結構實例的名稱。下面來看一個例子如代碼4-21所示,瞭解一下索引器是如何使用的。
4.4.3 索引器與屬性的區別
4.5 迭代器(Iterator)
• 迭代器是開發人員可以在類或結構中遍歷各自的數據結構的一種功能,能夠用兩種技術來實現,即foreach語句和迭代器。
4.5.1 foreach語句
• 【本節示例參考:\示例代碼\Chap04\TestIterator1】
• foreach語句在目的上和for語句類似,都是用來迭代數組或另外集合的元素。若是要對一個類使用foreach,那麼必須確保這個類提供了foreache所必須的GetEnumerator、MoveNext、Reset和Current成員。這些成員分別在IEnumerable和IEnumerator接口裏定義。因此,只要實現了這兩個接口就能夠對其用foreach語句。IEnumerable接口聲明一個方法支持對集合進行簡單的遍歷,的定義格式爲:
• public interface IEnumerable
• 公有實例方法的定義以下:
• IEnumerable GetEnumerator()
4.5.2 迭代器
• 若是類要實現GetEnumerator、MoveNext、Reset和Current成員,才能與foreach兼容實現迭代,那就太麻煩了,因此C# 2008提供了關鍵字yield來實現迭代器。只要類中提供迭代器,便可遍歷類中的數據結構。代碼演示瞭如何使用迭代器。
• 【本示例參考:\示例代碼\Chap04\TestIterator2】
4.6 小結
• 要理解面向對象,最重要的是要理解其中的一些基本概念,例如類和接口等。本節詳細的介紹了面向對象的一些概念類、接口、屬性和域和索引器。
• 雖然這些概念很難懂,可是它們倒是進入面向對象的第一步。要想學好面向對象編程,關鍵就要學好這一節。