第七部分 1 C#程式設計 – 南華大學資管系 C# 程式設計 程式架構(programming structures)1/3 傳統程序式程式設計(Procedural programming) C#程式設計 將程式劃分為「Data」和「程式碼」兩個獨立的部分 (資料 的宣告和函式的定義散佈在整個程式,只有透過文件才能 指出它們之間的關聯)。 「結構化」和「模組化」解決程式碼的統整問題,卻沒有 顧慮到與資料整合。 不易維護,可重用性低,易出錯 (成本高)。 OOP (Object-oriented programming) Software-IC a piece of software an object Data Specification + combine tightly related functions (operating on these data ) Encapsulation 封裝 2 物件之間以一種名為「訊息傳送的方式溝通」(方法的呼叫) 程式架構 2/3 不可直 接接觸 an object 啟動 Methods • public • Interface • visible message (含訊息參數) C#程式設計 Data • private • Information hiding • invisible message . . . encapsulation 若要做到Software-IC,需要下列特性: Encapsulation (class, object, message) Inheritance Polymorphism (dynamic binding) 3 程式架構 3/3 C#程式設計 OOP的三大特性: Encapsulation Inheritance Polymorphism 透過這些概念所寫的程式,不僅可以大大的提供軟體 的可重用性,更可以使我們除錯更容易,寫作更有 效率。 4 Objects, Classes, and Instances 1/5 電腦可以作為一種模擬世界的工具。人類的學習和認知 行為,有相當的部分是建立在「具體化」、「抽象化」 和「分類」等過程。 C#程式設計 (a) 傳統程式語言: 被模擬事物的資料和行為 (如:計算、訊息交換、互動等)無法 有效地結合在一起。 (b) OOP: 將實體的某些特性「抽取」(abstract)出來以後,再予以對應。 並將其資料和處理方法通通整合於物件之內。 物件(object):外界真實事物的抽象對應 資料:代表物件的一些性質。 行為:物件能夠(被)作用的動作。 5 Objects, Classes, and Instances 2/5 真實事務有什麼特性、狀態、行為 -抽象化(abstraction),以便表達。 -根據這些抽象化後的特性與行為,我們可以依需要 C#程式設計 的不同加以適當的分類,而得到不同的類別 (Class)。 如:人、貓、狗、…-動物類 花、草、樹木、…-植物類 作者Class的instances 分類前 分類後 作者Class 讀者Class 6 0bjects 讀者Class的instances Objects, Classes, and Instances 3/5 每一個東西都是objects, 分類後的各類叫做Class 屬於其Class的objects稱之為該Class的instances C#程式設計 Every object is an instance of a class Classes and Instances class:由一群具有某些相同資料結構與相同行為的物件 經分類後所形成的集合。 instance:由class所產生的具有某些資料結構與行為的 物件。 同一個class的instance都具有相同的資料格式與相同的 操作程序。但資料格式的實際內容(值)則未必相同。 7 Object-Oriented Programming 1/5 C#程式設計 在OOP理論中的軟體系統,是由一群同心協力 的物件(可能屬於不同class),合力組織、分層 負責來完成所有的工作。 Encapsulation(封裝) 軟體複雜度日增,為了增加可重用性與易維護性, 將資料與相關的操作方法封裝成物件。外界無法直 接存取內部私人的資料,只能透過物件提供的公開 的窗口存取內部維護的資料。物件之間的互動經由 message的傳遞,啟動某些操作程序來達成。 8 Object-Oriented Programming 2/5 同樣的資料格式與操作程序(方法),不必在每個需要的 地方都重寫一次,只要歸納(分類)成一個class,再依需 要重製出所需的instance即可。 C#程式設計 Inheritance (繼承) 是否可以把class再分類呢? 如: 人、豬、狗-動物類 生物類 花、草、樹木-植物類 這個分類再分類的性質,就是所謂的繼承性。我們可以 在不同的class之間,歸納分類出一些共同的特性,再形 成一個class。 幾個class共同的部分,就可以藉由繼承的功能,移轉到另一個 較高階的class去。 Class本身的定義可以精簡化。 Class形成階層組織(hierarchy)。 9 Object-Oriented Programming 3/5 OA1, OA2, …, OAm OB1, OB2, …, OBn abstraction abstraction Class B D1, D2, D3, D4 M1, M2, M3 D1, D3, D5, D6 M1, M2, M4 Class C Super class Subclass Class A’ D2, D4 M3 繼承 D1, D3 M1, M2 C#程式設計 Class A Instances: OA1, OA2, …, OAm, OB1, OB2, …, OBn 繼承 Class B’ 10 OA1, OA2, …, OAm D5, D6 M4 OB1, OB2,…, OBn Object-Oriented Programming 4/5 Employee Programmer Subclass C#程式設計 Manager Super class ”is-a” rule:every object of the subclass is an object of the super class (the opposite is not true) you can use a subclass object whenever the program expects a super class object (polymorphism) 11 Object-Oriented Programming 5/5 Polymorphism (多型、同名異式、Dynamic Linking) C#程式設計 message 是被送到物件介面,用來啟動物件的工具,而 method 則是message送達介面以後,被選取的操作程序 一旦物件產生之後,要使用這個物件只要單純的把訊息送 給它。只有在程式執行時,才會真正鎖定需要的物件,並 將message和appropriate method連接在一起 (並非在 compiling time 就將它們的關係固定下來。) 12 物件 (OBJECT) 所有objects的共同特徵就是state(狀態)和behavior(行為)。例 如:「車子」的狀態包括速度、傳動方式是兩輪或四輪傳 動、是否開燈等;「車」的行為則包括轉方向盤、踩剎車和 加速等等。 real-world object software object state data behaviors Methods objects 讓 programmer以較容易,且更合乎邏輯的方式來解 決真實世界的問題。 C#程式設計 物件(object)如同真實世界的物體一樣,物體的狀態 資料可看成屬性(property、data member),物件的行 為動作可視為方法(method、member function)。簡單 地說,物件為方法+屬性的整合。 13 類別的定義 (P.16-7) 1/8 [存取範圍修飾字] class 類別名稱 { // 主要成員(member)有屬性,方法和事件 C#程式設計 // 方法或屬性的宣告 (預設存取為private) // 通常屬性宣告為private變數 (encapsulation) // 方法(method)一定包含在類別之內,無法獨 立存在 } 14 類別的定義 2/8 定義MyTime類別,包含Hour, Minute, Second三個 屬性 C#程式設計 class MyTime { int Hour; int Minute; int Second; } 方案右鍵 > 加入 > 類別 > 名稱:MyTime.cs . . . namespace Project { class MyTime { … } } 15 類別的定義 3/8 建立物件: 使用new運算子,依照類別藍圖來建立物件(配置 MyTime now = new MyTime(); C#程式設計 記憶體),傳回指向此物件的參考。 此時物件變數now所參考的物件,稱為類別 MyTime的一個實例或實體 (instance) 所有產生的物件均各自獨立分配到一份記憶體, 彼此不互相干擾。(實體變數) 16 類別的定義 4/8 成員的存取:(在表單類別的button_Click事件程序中) 物件(變數)名稱.成員名稱 now.Hour = 10; now.Second = 30; C#程式設計 now.Minute = 30; 產生編譯錯誤 ‘ Project.MyTime.data’ 的保護層級導致無法對其進行存取。 Why?? 因為成員的預設存層級為private (只能在同一類別中被存取) (修正)將類別資料成員的存取層級改為public。 可被任何類別存取 17 類別的定義 5/8 class MyTime { public int Hour; public int Second; C#程式設計 public int Minute; } -即可通過編譯 - 將物件內容輸出, OK - now.Hour = 30; 合理嗎?? 如何檢查?? 18 類別的定義 6/8 物件的封裝性(Encapsulation) 將保護的資料成員加上private存取修飾,宣告其僅供物件 內部使用。(information hiding) C#程式設計 保護物件的資料成員,不要讓物件範圍以外的人有任何 更改資料進而破壞資料完整性的可能。 透過公開(public)或保護(protected)的方法管道,允許物件 外部的人能間接存取private的資料成員。 將private之資料成員與public之成員方法封裝起來,即為 encapsulation。 19 類別的定義 7/8 具封裝性的MyTime類別 class MyTime { private int Hour; private int Second; C#程式設計 private int Minute; public string getTime( ) { return Hour + ”:” + Minute + ”:” + Second; } public void setTime(int h, int m, int s) { Hour = h; Minute = m; Second = s; } } 20 類別的定義 8/8 now.Hour = 30; // X, 不能通過編譯 必須通過開放的method來進行 在setTime(~)內進行資料的檢查 public void setTime(int h, int m, int s) { C#程式設計 now.setTime(30, 30, 30); // 假設超出範圍,則不處理 if (h < 0 || h > 23) return; if (m < 0 || m > 59) return; if (s < 0 || s > 59) return; Hour = h; Minute = m; Second = s; } 21 練習:定義自己的類別Date 私有成員:day, month, year (可用的) 公開成員(原型prototype): public void setDate(int d, int m, int y); public string show( ); //回傳的格式 ”m-d-y” C#程式設計 class Date { private int day; private int month; private int year; public void setDate(int d, int m, int y) { day = d; month = m; year = y; } public string show( ) { return month + “-” + day + “-” + year; } } 22 練習:類別Date的使用 (測試) 輸出鈕 int y = Convert.ToInt32(txtYear.Text); int d = Convert.ToInt32(txtDay.Text); C#程式設計 int m = Convert.ToInt32(txtMonth.Text); Date date = new Date(); date.setDate(d, m, y); lblOutput.Text = date.show(); 23 方法的多載(Method Overloading) P.16-18 練習:在類別MyTime中新增另一個setTime方法 C#程式設計 在C#的類別中允許定義兩個以上同名方法,但其傳 遞的參數個數或資料型態不同即可,此即「多載」 (overloading)。 1. public void setTime (int h, int m) { if(h < 0 || h > 23) return; if(m < 0 || m > 59) return; Hour = h; Minute = m; Second = 0; } 2. MyTime obj2 = new MyTime( ); obj2.setTime (21, 40); 24 類別的建構子(Constructor) P.16-21 建構子是特殊的方法,在物件建立的同時,建構子會自動被 呼叫,此方法通常用於設定資料成員的初值。 預設建構子 C#程式設計 類別的建構子名稱必須和類別名稱相同。而且,建構子沒有 回傳值(也不加上void) 。通常是使用public修飾子進行宣告 建構子支援多載 public MyTime(int h, int m, int s) {…...} public MyTime(int h, int m) {…..} 為了統一檢查資料,可以將其獨立為一個private method private bool validTime(int h, int m, int s); 25 新版的class MyTime 1/2 class MyTime { private int Hour; private int Minute; public MyTime(int h, int m, int s) { // constructor if( ValidTime(h, m, s) ) { Hour = h; Minute = m; Second = s; } } public MyTime(int h, int m) { // constructor if ( ValidTime(h, m, 0) ) { Hour = h; Minute = m; Second = 0; } } C#程式設計 private int Second; 26 新版的class MyTime 2/2 … C#程式設計 private bool validTime(int h, int m, int s) { if (h < 0 || h > 23) return false; if (m < 0 || m > 59) return false; if (s < 0 || s > 59) return false; return true; // 合法資料 } } // class MyTime MyTime t1 = new MyTime(9, 30, 50); MyTime t2 = new MyTime(21, 40); 27 This和This()的使用 this關鍵字:指目前作用中的物件。在類別內呼叫同 類別的實體成員可加上this,通常可省略,有必要區 分時,才加上this。 this.Hour = Hour; C#程式設計 public void setTime(int Hour, int Minute, int Second) { this.Minute = Minute; this.Second = Second; } this(~):呼叫同類別的令一建構子 public MyTime(int h, int m) : this(h, m, 0) { } 28 解構子(Destructor) 在物件(的記憶體)被釋放前,會自動被呼叫的方法。一般 用來釋放物件中佔用的檔案、網路、資料庫等資源。 C#程式設計 一般而言,C#程式並不需要進行太多的記憶體管理,因為 C#會自動進行記憶體回收的動作,將不會再被使用的物件 記憶體回收。(未必立即進行回收) 解構子的名稱是在類別名稱前加上“~” 所構成。 ~MyTime() { //不可加上public MessageBox.Show(“*** destructor ***”); } // 必須Using System.Windows.Forms; 29 物件陣列 多個同類型或相容物件所構成的陣列 C#程式設計 MyTime tArray = new MyTime[3]; tArray[0] = new MyTime( ); tArray[0].setTime(21, 40); tArray[1] = new MyTime(9, 30, 50); tArray[2] = new MyTime(10, 30, 30); MyTime tArray ● ● ● H 21 M 40 S 0 ● H 9 H 10 M 30 M 30 S 50 S 30 30 練習:利用for迴圈印出物件陣列的內容 練習:class Date 1. 在class Date內加上兩個constructors public Date() { // 自己定義default constructor } C#程式設計 day = 1; month = 1; year = 200; public Date(int d, int m, int y) { day = d; month = m; year = y; } 2. // 利用TextBox輸入d, m, y Date date1 = new Date(d, m, y); lblOutput.Text = date1.show(); 31 UML (Unified Modeling Language)之類別圖P.16-6 類別名稱 (class name) 成員方法 (member function) -day:int -month:int -year:int +<<Constructur>> Date() +<<Constructur>> Date(int, int, int ) +setDate(int, int, int) +show():string C#程式設計 成員資料 (Data member) Date +: public -: private #: protected 32 練習:定義類別Person (1/4) 類別名稱:Person 成員資料 name : string // unknown (預設值) – age : int – gender : char // ‘M’ – date : Date // 19 C#程式設計 – // (1, 1, 2000)內含物件成員 // has-a relationship 33 練習:定義類別Person (2/4) 成員方法 +<<constructor>> Person() +<<constructor>> Person(string, int, char) +setName(string) +setAge(int) C#程式設計 +<<constructor>> Person(string, int, char, Date) +setGender(char) +setDate(Date) +getName() : string +getAge() : int +getGender() : char +getDate() : Date +show() : string //格式: 名字 = Tom 年齡 = 21 性別 = 男 生日 = 10-30-1990 34 練習:定義類別Person (3/4) class Person { private string name; private int age; private char gender; private Date date; public void setName(string n) { name = n; } public string getName() { return name; } public void setAge(int a) { age = a; } C#程式設計 public Person() { name = "unknown"; age = 19; gender = 'M'; } public Person(string n, int a, char g) { name = n; age = a; gender = g; } public Person(string n, int a, char g, Date d) { name = n; age = a; gender = g; date = d; } 35 練習:定義類別Person (4/4) } C#程式設計 public int getAge() { return age; } public void setGender(char g) { gender = g; } public char getGender() { return gender; } public void setDate(Date d) { date = d; } public Date getDate() { return date; } public string show() { string str = "名字 = " + name + "\r\n"; str += "年齡 = " + age + "\r\n"; str += "性別 = " + gender + "\r\n"; str += "生日 = " + date.show(); return str; } 36 Encapsulation C#程式設計 因為Encapsulation的特性,object可被視為獨立的「 黑盒子」,他可以執行特定功能,我們只要知道可以 傳給盒子什麼東西,以及可以得到什麼結果,而不必 知道其內部的implementation(實作)。也就是說, 資訊從黑盒子的輸入端傳入,而從輸出端產生結果, 我們不必知道也不用關心其內部運作的情形 可以串接或整合數個「黑盒子」,它們各自執行其特 定功能,因此建立一個大而複雜的系統。由於 Encapsulation的特性,我們可以在不影響系統工作的 前提下,自由地替換另一更理想(效率)的「黑盒子 」。 37 類別Person的使用 string name = txtName.Text; int age = Convert.ToInt32(txtAge.Text); char gender = '男'; C#程式設計 if(rdbMale.Checked) gender = '男'; if(rdbFemale.Checked) gender = '女'; int y = Convert.ToInt32(txtYear.Text); int m = Convert.ToInt32(txtMonth.Text); int d = Convert.ToInt32(txtDay.Text); Date date = new Date(d, m, y); Person p = new Person(name, age, gender, date); lblOutput.Text = p.show(); 38 類別的靜態成員 (P.16-25) C#類別可以宣告靜態成員,這是一種屬於類別本身的成員 ,不需要產生物件即可使用。因此,可以將一些不會因個 別物件而有所差異的成員宣告為靜態。 靜態資料成員的記憶體僅有一份,由該類別所產生的所有物件共享 C#程式設計 類別成員的宣告若未加上static,則稱為實體(instance)成員;若加 上static,則稱為靜態成員。 實體成員的存取 物件名稱.實體成員名稱 靜態成員的存取 類別名稱.靜態成員名稱(如:Convert.ToInt32(~)) 物件名稱.靜態成員名稱? (C#: X) 類別名稱.實體成員名稱(X) 39 在類別Person中新增靜態成員 記錄並且取得共產生多少個Person物件 有底線者為static member Person ctr記錄物件實體的個數 --– ctr : int(初始值為ø) private static int ctr = 0; --+ counter() : int C#程式設計 counter()回傳目前已產生的物件數(ctr) 個數的紀錄:在每個Constructor中加入ctr++; public static int counter() { return ctr; } 在表單中隨時更新Person物件的個數 “共有” + Person.counter() + ”人” 40 namespace(名稱空間) (P2-23, P.7-31) 我們以using statement來描述在每個程式裡所使用到的 namespace。 C#程式設計 namespace又稱為命名空間,可以建立類別的群組,方便 對大量的類別以階層式的結構進行分門別類,將相關的類 別放在一起,以利管理。每一群組以一個名稱來代表,能 夠減少類別名稱相同所產生的衝突。 例如:using System; 告訴編譯器我們使用System namespace,如此,允許我們在程式 中寫Console.WriteLine(~),而不使用 System.Console.WriteLine(~)。 同一專案中的程式碼,以專案名稱作為預設的名稱空間 41 .NET Framework類別函式庫 是一個龐大且良好組織結構架構的函式庫。其類別架構是使用稱為 namespace的階層類別架構,每一個名稱空間能夠擁有多個類別。 C#程式設計 每一階層的namespace是使用「.」運算子連接 使用using 名稱空間; 匯入所需的名稱空間,可以方便使用其內的類別 using System.Window.Forms; 42