一、基礎術語
1、對象
對象是具有資料、行為和辨別的三部分程式設計結構。對象的資料包含在對象的字段、屬性、事件中。對象的行為則由對象的方法和接口定義。對象通過classe和struts定義。對象通過辨別來區分是否相同。對象都是從類和結構中定義的模闆執行個體化得到。均繼承于Object類。
2、類
類定義了資料類型的資料和行為。可以根據這些來建立類的執行個體化對象。類支援繼承但不可以多繼承。類屬于引用對象。最終類可以有很多個基類(類繼承基類而導緻)。可以實作多個接口。類和對象本質有不同,類是對象的類型,對象是類的執行個體。
3、分部類
分部類可以将類、結構、或接口的定義拆分到兩個或多個源檔案中,每個源檔案都包括類定義的一部分,編譯時把所有部分組合起來。使用partial定義。
4、結構
結構:定義基本于類相同,結構聲明時,如果字段不是const或static,就無法初始化。結構不可以聲明預設的構造函數(無參構造函數)或析構函數。可以聲明帶有參數的構造函數。結構副本由編譯器自動建立和銷毀,故不需要。結構不可以從類或其他結構繼承。屬于值類型,結構執行個體化不可以使用new,不可以繼承,直接繼承自System.ValueType。可以實作接口。
5、繼承
類繼承通過:号定義,:号後為要繼承的單一的基類。由基類派生的類将會獲得基類所有非私資料和行為(方法)以及派生類為自己定義的所有其它資料或行為(方法),是以派生類有兩個有效類型:派生類型和基類類型。
6、抽象類
使用abstract定義用來繼承的類和成員。抽象類不可以執行個體化,用來提供多個派生類可以共享的基類的公共定義(有點類似接口的意思),抽象類可以定義抽象方法,用abstract定義,但不可以有方法的實作。由抽象類派生的類必須實作抽象方法。
7、密封類
使用sealed關鍵字來定義防止繼承以前标記的為virtual的類或某些成員。密封類主要用來防止派生。
8、多态
一個類可以用作多種類型,可以作自己的類型,可以做基類,可以實作接口時用作任何接口類型。
9、接口
接口使用interface定義,可以描述屬于任何類或結構的一組相關行為,可由方法、屬性、事件、索引器或這四種成員類型的任何組合構成。接口不可以包含字段,必須為公共的。可以同時繼承多個接口,隻能繼承到方法名稱和簽名。
10、類成員
包括字段、屬性、方法、事件、運算符、索引器、構造函數、析構函數、嵌套類型。
11、方法
方法包括一些類語句的代碼塊,方法在類或結構中聲明,需要指定通路級别,傳回值,方法名稱以及任何方法參數。
12、構造函數
類和結構可以有多個接受不同參數的構造函數,可以使得可以設定預設值,限制執行個體化等功能。
13、析構函數
隻能對類定義析構函數,并且一個類的析構函數唯一,無法繼承和重載,無法調用,無修飾符和參數。
14、字段
包含在類或結構中的對象和值,字段可以使類和結構封裝資料。一般用private,通過方法、屬性或索引器通路字段。
15、常量
類和結構可以将常數聲明為成員,用const修飾。
16、嵌套類型
在類或結構中定義的類型為嵌套類型,預設為private可以設定為其它修飾符,
17、靜态類
靜态類和類成員用于建立無需建立類的執行個體就能夠通路的資料和函數,靜态類成員可以用于分離獨立于任何對象辨別的資料和行為。
18、面向對象的思想主要包括:繼承 多态 封裝
封裝:用抽象的資料類型将資料和基于資料的操作封裝在一起,資料被保護在抽象資料類型内部。
繼承:子類擁有父類的所有資料和操作。
多态:一個程式中同名的不同方法共存的情況。
二、名詞解釋
1、.NET Framework(.NET 架構)
.NET 架構是Microsoft公司的一種新的便捷開發平台。.NET 架構有兩個核心元件: CLR(公共語言運作庫)和FCL(.NET Framework 類庫)。.NET 架構好比一個航母,他支援的開發語言(C#、VB.NET、J#)就像航母上的戰鬥機。
2、CLR(Common Language Runtime)公共語言運作時
是 .NET Framework 的基礎、相當于Java虛拟機。可以将運作庫看作一個在執行時管理代碼的代理,它提供記憶體管理、線程管理和遠端處理等核心服務,并且還強制實施嚴格的類型安全以及可提高安全性和可靠性的其他形式的代碼準确性。事實上,代碼管理的概念是運作庫的基本原則。以運作庫為目标的代碼稱為托管代碼,而不以運作庫為目标的代碼稱為非托管代碼。
3、FCL(Framework Class Library).NET架構類庫
它提供了大量的實用類,如:System.console、System.IO等,它是一個綜合性的面向對象的可重用類型集合,可以使用它開發多種應用程式,這些應用程式包括傳統的指令行或圖形使用者界面 (GUI) 應用程式。
4、MSIL(Microsoft Inter Language)微軟中間語言
所有面向.NET的語言都要先編譯成IL, MSIL是将.Net代碼轉化為機器語言的一個中間過程。當使用者編譯一個.Net程式時,編譯器将源代碼翻譯成一組可以有效地轉換為本機代碼且獨立于CPU 的指令。當執行這些指令時,實時(JIT)編譯器将它們轉化為CPU特定的代碼。這是為什麼.NET能支援多種開發語言的原因。
5、CTS(Common Type System) 通用類型系統
不同程式設計語言定義的基本資料類型各不相同。既然要實作語言之間的“溝通”,就需要一個規範來實作“不同”資料類型(也許隻是名稱不同,而實質相同)間的互動操作,CTS就完成這個工作。如C#使用VB.NET開發的類庫。
6、CLS(Common Language Specification) 公共語言規範
.NET平台上所有語言都應遵守的規則,用集合來說是多個語言規則的交集。如:在C# 中命名是區分大小寫的,而VB.NET不區分大小寫,這樣CLS就規定,編譯後的中間代碼必須除了大小寫之外有其它的不同這處。
7、JIT(Just-In-Time)
将MSIL編譯成計算機能執行的二進制碼。強調實時,實時執行時針對本地計算機的不同,被編譯成本機代碼。
8、Assembly程式集
是.NET程式的最小組成機關。每個程式集都有自己的名稱、版本等資訊。程式集通常表現為一個檔案(.exe或.dll檔案),這樣的程式集被稱為單檔案程式集,這種程式集是最常見的。程式集也可以由多個檔案組成,每個檔案都是一個子產品檔案或者是一個資源檔案,這樣的程式集被稱為多檔案程式集。
三、比較記憶
1、通路修飾符。
public(公共的) | 通路不受限制 | 如果對類不指定通路修飾符,則類的預設通路修飾符為internal,但是類成員的預設通路修飾符為private。類隻能修飾為public或internal類成員可以是所有。 |
protected (保護的) | 通路範圍限定于它所屬的類或從該類派生的類型 | |
internal(内部的) | 同一程式集中的任何代碼都可以通路該類型或成員 | |
protected internal(内部保護的) | 通路範圍限定于同一程式集或那些由它所屬的類派生的類型 | |
private(私有的) | 通路範圍限定于它所屬的類型 |
2、類與對象
一切事物皆為對象,類是一組具有相同屬性和方法的對象的抽象,對象是類的實體,類是對象的抽象。對象是包含資料和操作的實體,它既定義資料元素,又定義可應用這些資料元素的操作。類是對一組具有相同屬性和行為的對象的描述,類的内容稱為類的成員。
3、靜态類與非靜态類
所謂靜态類即在類的前面用static關鍵字來修飾的類。這樣的類不能被執行個體化、是密封類、僅包含靜态成員、不包含執行個體構造函數,靜态類不能使用new建立執行個體,用類名就可能直接通路其成員,非靜态類則不行。靜态類中不能建立非靜态的方法。即靜态方法中隻能建立靜态方法,但在非靜态類中可以調用靜态方法,也可以建立靜态成員,包括方法。
4、程式集和命名空間
可以把程式集簡單了解為你的.NET項目在編譯後生成的*.exe或*.dll檔案,一個程式集可以跨越n個命名空間,一個命名空間也可以包含n個程式集。如果說命名空間是類庫的邏輯組織形式,那麼程式集就是類庫的實體組織形式。
5、Const與Readonly
(1)、const 字段隻能在該字段的聲明中初始化。readonly 字段可以在聲明或構造函數中初始化。是以,根據所使用的構造函數,readonly 字段可能具有不同的值。(2)、const 字段是編譯時常數,而 readonly 字段可用于運作時常數。(3)、const 預設就是靜态的,而 readonly 如果設定成靜态的就必須顯示聲明。(4)、const 對于引用類型的常數,可能的值隻能是 string 和 null。readonly可以是任何類型
6、類與結構
(1)、結構是值類型,而類是引用類型。在結構中初始化字段是錯誤的。
(2)、向方法傳遞結構時,結構是通過傳值方式傳遞的,而不是作為引用傳遞的。
(3)、與類不同,結構的執行個體化可以不使用 new 運算符。結構儲存在棧上,而類儲存在受管制的堆上。
(4)、結構不能聲明預設構造函數或析構函數。結構可以聲明帶有參數的構造函數。
(5)、一個結構不能從另一個結構或類繼承,而且不能作為一個類的基。類是重量級的,而結構是輕量級的。
(6)、所有結構都直接繼承自 System.ValueType,所有的引用類型繼承自System.Object。
(7)、類與結構都可以實作接口,都包括字段與方法。結構的執行效率更高。
7、裝箱和拆箱
裝箱(boxing)和拆箱(unboxing)機制使得在C#類型系統中,任何值類型、引用類型和object(對象)類型之間進行轉換,這種轉換稱為綁定連接配接。裝箱:将值類型轉換成引用類型,拆箱:将引用類型轉換成值類型。
8、接口和類
接口可以多繼承,類隻能單繼承。接口描述可屬于任何類或結構的一組相關行為。接口可由方法、屬性、事件、索引器或這四種成員類型的任何組合構成。接口不能包含字段。接口成員一定是公共的。類 隻支援單繼承:類隻能從一個基類繼承實作。一個類可以實作多個接口。
9、值類型和引用類型
值類型和引用類型的差別在于,值類型的變量直接存放實際的資料,而引用類型的變量存放的則是資料的位址,即對象的引用。值類型變量直接把變量的值儲存在堆棧中,引用類型的變量把實際資料的位址儲存在堆棧中,而實際資料則儲存在堆中。類、數組、接口、委托是引用類型,struct與enum是值類型。
10、重載與override
規則 | 重載(overload) | 覆寫(override) |
存在位置 | 存在于同一個類中 | 存在于有繼承關系的不同類中 |
調用機制 | 編譯時确定 | 運作時确定 |
方法名 | 必須相同 | 必須相同 |
參數清單 | 必須不同 | 必須相同 |
傳回值類型 | 可以不相同 | 必須相同 |
泛型方法 | 可以重載 | 可以覆寫 |
11、抽象類(abstract class)與接口(interface)
相同點:1、都不能被執行個體化,都可以通過繼承實作其抽象方法;2、都是面向抽象程式設計的技術基礎;
不同點:1、接口支援多繼承,也就是說一個類可以實作多個接口;抽象類不能實作多繼承;2.接口隻能定義抽象規則或對象;抽象類即可以定義抽象的,也可以提供一些已經實作的成員或方法;3、接口隻包含方法、屬性、索引器、事件的簽名,但卻不能定義字段和包含實作的方法;抽象類可以定義字段、屬性和包含有實作的方法;4.接口屬于值類型;抽象類屬于引用類型;
12、虛方法(virtual)與抽象(abstract)方法
相同:都可以在派生類中重寫;不同點:虛方法在基類中有實作的部分,在派生類中可重寫也可不重寫;而抽象方法在基類中沒有實作的部分,在派生類中必須重寫;抽象方法隻能在抽象類中,抽象類中可以有實休方法。
四、典型例子
namespace T82 //命名空間
{
public interface objects //定義接口
{
void Objinterface(); //接口中的方法成員,不能有方法體
}
abstract class People:objects //抽象類,沒有通路修飾符預設為internal,實作objects接口
{
const string className = "People"; //常量,預設是靜态的
public People() { } //構造方法
public People(string _name) { this.Name = _name; } //方法重載
private string name; //字段
public string Name //屬性
{
get { return name; }
set { name = value; }
}
public virtual void SayHi() //虛方法
{
Console.WriteLine("Hi,"+this.Name);
}
public abstract void ShowDemo(string DemoString); //定義抽象方法,必須在抽像類中,不能有方法體
public static void ClassName() //靜态方法,使用到的成員必須是靜态的
{
Console.WriteLine(className); //顯示常量
}
public void Objinterface() //必須實作objcets接口的方法
{
Console.WriteLine("Objinterface()");
}
}
class Student:People //繼承,
{
public Student(string _name):base(_name){} //調用父類構造方法
public override void SayHi() //重寫虛方法
{Console.WriteLine("Hello," + this.Name);}
private string[] nickName=new string[2]; //字段
public string this[int index] //定義索引器
{
get { return nickName[index]; }
set { nickName[index] = value;}
}
public override void ShowDemo(string DemoString) //重寫抽象方法
{ Console.WriteLine(this.Name + " is a " + DemoString);}
}
public class Client
{
public static void Main(string[] args)
{
Student s1 = new Student("zhangSan"); //執行個體化一個學生
Student.ClassName(); //靜态方法通過類名調用
s1[0] = "SanSan"; //通過執行個體名通路字段
s1[1] = "zhangzhang";
Console.WriteLine(s1.Name+"叫"+s1[0] + "也叫"+s1[1]);
s1.SayHi(); //非靜态方法通過執行個體.調用
s1.ShowDemo("Top Student"); //通過實調用已經實作的抽象方法
s1.Objinterface(); //通過s1通用已實作的方法
Console.ReadKey();
}
}
}
面向對象的特性
各種面向對象程式設計語言互相有别,但都能看到它們對面向對象三大機制的支援,即: “封裝、繼承、多态”
– 封裝,隐藏内部實作
– 繼承,複用現有代碼
– 多态,改寫對象行為 使用面向對象程式設計語言(如C#),可以推動程式員以面向對象的思維來思考軟體設計結構,進而強化面向對象的程式設計範式。
C#是一門支援面向對象程式設計的優秀語言,包括:各種級别的封裝支援;單實作繼承+多接口實作;抽象方法與虛方法重寫。
封裝對象的原理
封裝是把類的内部隐藏起來,以防止外部世界看見的一個面向對象的概念。
1、通過隻是使用那些對外公有的類成員(public标示的),可以盡可能的減少依賴
2、減少了其他人破壞程式代碼的可能性
3、使用者隻能看到那些對外公有的成員,清晰的展現了此類對外開放的接口,易于其他類的調用。
封裝性最有用的方式之一
實作方法——通路限制修飾符
public 無限制,允許任何人來通路
protected internal = protected + internal 允許程式集内部或繼承通路
Internal 允許項目或程式内部的類來通路
protected 繼承時子類可以對基類有完全通路權
private 隻允許同一個類中的成員通路
屬性和索引器也用來封裝類的細節,并提供公用接口給該類的使用者
繼承
一個類可以有能力直接從另一個類獲得其代碼和資料
派生類從基類那裡獲得其所有的成員
例:TextBox繼承自 System.Web.UI.WebControls.WebControl繼承自System.Web.UI.Control
如何實作繼承?
public class InsuranceAccount : Account
C# 中隻支援單繼承
防止繼承
public sealed class YY

class Point
... {
private int x;
public int X
...{
get ...{ return x; }
set ...{ x = value; }
}
private int y;
public int Y
...{
get ...{ return y; }
set ...{ y = value; }
}
public Point(int x, int y)
...{
this.x = x;
this.y = y;
}
}

lass Point3D:Point
... {
private int z;
public int Z
...{
get ...{ return z; }
set ...{ z = value; }
}
public Point3D(int x, int y, int z)
: base(x, y)
...{
this.Z = z;
}
}
程式講解
類的聲明可能通過在類名後加上冒号和基類的名字來指定一個基類譯注4。省略基類等同于直接從object類派生。在下面的示例中,Point3D的基類是Point,而Point的基類是object:
Point3D類繼承了其基類的成員。繼承意味着類将隐式地包含其基類的所有成員(除了基類的構造函數)。派生類能夠在繼承基類的基礎上增加新的成員,但是它不能移除繼承成員的定義。在前面的示例中,Point3D類從Point類中繼承了x字段和y字段,并且每一個Point3D執行個體都包含三個字段x,y和z。
如何通路基類成員?
派生類可以調用基類的方法
通過使用base關鍵字
派生類在通路基類的時候有一定的限制,不能通路 private 的成員;internal的基類成員隻能被同一個程式集中的派生類通路

ublic class Account
... {
public double balance;
public bool ;
public Withdraw(double amt)
...{
balance -= amt;
return true;
}
}


public class CheckAccount:Account
... {
public bool Withdraw(double amt)
...{
if(amt<= base.balance)
return base.Withdraw(amt);
else
return false;
}
}
調用基類構造函數
因為不能繼承構造方法,派生類必須實作自己的構造方法
public child(int a,int b,int c):base(a,b)
{
}
多态性(1)
面向對象程式設計中的另外一個重要概念是多态性。在運作時,可以通過指向基類的引用,來調用實作派生類中的方法。當然,如果它們都繼承自某個類,你可以把這些派生類,都放到一個數組中。如果這些對象都有同名方法,就可以調用每個對象的同名方法。
同一操作作用于不同的對象,可以有不同的解釋,産生不同的執行結果,這就是多态性。多态性通過派生類覆寫基類中的虛函數型方法來實作。
在面向對象的系統中,多态性是一個非常重要的概念,它允許客戶對一個對象進行操作,由對象來完成一系列的動作,具體實作哪個動作、如何實作由系統負責解釋。
“多态性”一詞最早用于生物學,指同一種族的生物體具有相同的特性。在C#中,多态性的定義是:同一操作作用于不同的類的執行個體,不同的類将進行不同的解釋,最後産生不同的執行結果。
多态性通過派生類覆寫基類中的虛函數型方法來實作。
多态性(2)
編譯時的多态性
編譯時的多态性是通過重載來實作的。對于非虛的成員來說,系統在編譯時,根據傳遞的參數、傳回的類型等資訊決定實作何種操作。
運作時的多态性
運作時的多态性就是指直到系統運作時,才根據實際情況決定實作何種操作。
C#中,運作時的多态性通過覆寫虛成員實作。
虛拟函數
聲明虛方法
使用virtual關鍵字 public virtual bool Withdraw(…);
調用虛方法,運作時将确定調用對象是什麼類的執行個體,并調用适當的覆寫的方法。
虛方法可以有實作體
具體的檢查的流程如下:
1、當調用一個對象的函數時,系統會直接去檢查這個對象申明定義的類,即申明類,看所調用的函數是否為虛函數;
2、如果不是虛函數,那麼它就直接執行該函數。而如果有virtual關鍵字,也就是一個虛函數,那麼這個時候它就不會立刻執行該函數了,而是轉去檢查對象的執行個體類。
3、在這個執行個體類裡,他會檢查這個執行個體類的定義中是否有重新實作該虛函數(通過override關鍵字),如果是有,那麼OK,它就不會再找了,而馬上執行該執行個體類中的這個重新實作的函數。而如果沒有的話,系統就會不停地往上找執行個體類的父類,并對父類重複剛才在執行個體類裡的檢查,直到找到第一個重載了該虛函數的父類為止,然後執行該父類裡重載後的函數。

public class A
... {
protected internal int a=0;
public virtual void Func() // 注意virtual,表明這是一個虛拟函數
...{
Console.WriteLine("Func In A");
}
}

class B : A // 注意B是從A類繼承,是以A是父類,B是子類
... {
public override void Func() // 注意override ,表明重新實作了虛函數
...{
Console.WriteLine("Func In B");
}
}

class C : B // 注意C是從A類繼承,是以B是父類,C是子類
... {
}


class D : A // 注意B是從A類繼承,是以A是父類,D是子類
... {
public new void Func() // 注意new ,表明覆寫父類裡的同名類,而不是重新實作
...{
Console.WriteLine("Func In D");
}
}

static void Main( string[] args)
... {
//Point3D p3d = new Point3D(1, 2, 3);
//Console.WriteLine("x:{0},y:{1},z:{2}", p3d.X, p3d.Y, p3d.Z);
A a; // 定義一個a這個A類的對象.這個A就是a的申明類
A b; // 定義一個b這個A類的對象.這個A就是b的申明類
A c; // 定義一個c這個A類的對象.這個A就是b的申明類
A d; // 定義一個d這個A類的對象.這個A就是b的申明類
a = new A(); // 執行個體化a對象,A是a的執行個體類
b = new B(); // 執行個體化b對象,B是b的執行個體類
c = new C(); // 執行個體化b對象,C是b的執行個體類
d = new D(); // 執行個體化b對象,D是b的執行個體類
Console.WriteLine("a.Func() ");
a.Func();
//// 執行a.Func:1.先檢查申明類A 2.檢查到是虛拟方法 3.轉去檢查執行個體類A,就為本身 4.執行執行個體類A中的方法 5.輸出結果 Func In A
Console.WriteLine("b.Func() ");
b.Func();
//// 執行b.Func:1.先檢查申明類A 2.檢查到是虛拟方法 3.轉去檢查執行個體類B,有重載的 4.執行執行個體類B中的方法 5.輸出結果 Func In B
Console.WriteLine("c.Func() ");
c.Func();
//// 執行c.Func:1.先檢查申明類A 2.檢查到是虛拟方法 3.轉去檢查執行個體類C,無重載的 4.轉去檢查類C的父類B,有重載的 5.執行父類B中的Func方法 5.輸出結果 Func In B
Console.WriteLine("d.Func() ");
d.Func();
//// 執行d.Func:1.先檢查申明類A 2.檢查到是虛拟方法 3.轉去檢查執行個體類D,無重載的(這個地方要注意了,雖然D裡有實作Func(),但沒有使用override關鍵字,是以不會被認為是重載) 4.轉去檢查類D的父類A,就為本身 5.執行父類A中的Func方法 5.輸出結果 Func In A
D d1 = new D();
d1.Func(); // 執行D類裡的Func(),輸出結果 Func In D}
// Demo.多态.Shoot.ShootBird(new Demo.多态.Sparrow());
}
什麼時候用到虛函數
既然虛函數應為多态而生,那麼簡單的說當我們在C#中要想實作多态的方法之一就是使用到虛函數。複雜點說,那就是因為OOP的核心思想就是用程式語言描述客觀世界的對象,進而抽象出一個高内聚、低偶合,易于維護和擴充的模型。
但是在抽象過程中我們會發現很多事物的特征不清楚,或者很容易發生變動。
比如飛禽都有飛這個動作,但是對于不同的鳥類它的飛的動作方式是不同的,有的是滑行,有的要顫抖翅膀,雖然都是飛的行為,但具體實作卻是千差萬别,在我們抽象的模型中不可能把一個個飛的動作都考慮到,那麼怎樣為以後留下好的擴充,怎樣來處理各個具體飛禽類千差萬别的飛行動作呢?比如我現在又要實作一個類“鶴”,它也有飛禽的特征(比如飛這個行為),如何使我可以隻用簡單地繼承“飛禽”,而不去修改“飛禽”這個抽象模型現有的代碼,進而達到友善地擴充系統呢?
是以面向對象的概念中引入了虛函數來解決這類問題。
使用虛函數就是在父類中把子類中共有的但卻易于變化或者不清楚的特征抽取出來,作為子類需要去重新實作的操作(override)。而虛函數也是OOP中實作多态的關鍵之一。