###### tags: `C#` # **C#實作課程-Lesson 3** # 方法(method)(續Lesson 2) ## out string 參數 ```csharp= static void Main(string[] args) { Console.WriteLine("請設定起始車資:"); int money = int.Parse(Console.ReadLine()); Console.WriteLine("是否為夜間加成時段(+30元):(Y/N)"); bool isNight = (Console.ReadLine() == "Y" ? true : false); Console.WriteLine("是否使用後車行李箱(+10元):(Y/N)"); bool UseBox = (Console.ReadLine() == "Y" ? true : false); //比照方法須填入out string 參數 var TotalMoney=CalTaxi(money,out string detailstr,isNight,UseBox); Console.WriteLine($"車資一共為{TotalMoney}元~~"); Console.WriteLine(detailstr); Console.ReadLine(); } //建立方法時多設定一個out string參數,可用來回傳更多參數資訊 //(ex:此方法本來只能回傳一個int結果) static int CalTaxi(int money, out string detail,bool isNight = false, bool UseBox = false) { if (isNight){ money += 30; } if (UseBox) { money += 10; } //使用out string參數,下面這行必須要寫出來 detail = $"車資為{money},夜間加成為{isNight},使用行李箱為{UseBox}"; return money; } ``` ※類似用法:TryParse ```csharp= var w = "89.3"; if(int.TryParse(w,out int wval))//傳入w嘗試傳出wval { wval *= 2; } } ``` ## ref 參數 ```csharp= static void Main(string[] args) { var y = "ABC"; MyMethod(y); Console.WriteLine(y);//對照組;y值無改變 MyMethod2(ref y);//比照方法參數設定須加上ref Console.WriteLine(y);//使用ref參數方法會改變原始輸入值 Console.ReadLine(); } static void MyMethod(string x) { x = "DEF"; } static void MyMethod2(ref string x)//參數宣告加上ref { x = "DEF"; } ``` # 例外處理(try catch) ## 例外處理原則 1.可以用程式方式檢查出的問題,不要使用例外處理(效能問題) 2.catch到例外後務必進行處理(ex: throw),**catch內不要空白不處理** 3.throw的內容可再進行包裝(避免原始碼和錯誤訊息直接被User看到) ## 語法 try { //執行的程式碼邏輯 } catch { //如果try的區域發生錯誤,則執行catch裡面的內容 } finally { //無論如何都會執行到的程式碼(通常用於**資源釋放**) } ex: ```csharp= static void Main(string[] args) { try { var x = 1; var result = x / 0; } catch (Exception e) //將例外狀況放在e變數 { e.ToString();//,可將e轉成字串,後續可write to db.... throw ;//將例外狀況丟至上一層 } } ``` 偵錯結果如下 ![](https://i.imgur.com/xFY8ISC.png) ```csharp= static void Main(string[] args) { try { var x = 1; int result = x / 0; } catch (Exception e) { e.ToString();//write to db.... throw new Exception("System Error!!!");//可包裝錯誤訊息 } } ``` 偵錯結果如下 ![](https://i.imgur.com/XyIEca3.png) ## Exception類別 ●Exception ●SystemException ●ApplicationException ●NullReferenceException ●FileNotFoundException ●SerializationException ## 可針對不同Exception類別做不同處理 ex: ```csharp= try { //執行的程式碼邏輯 } catch(NullReferanceException ex) { // 針對NULLReferance的處理方式...... } catch(Exception ex) //其他例外的處理方式 finally { //無論如何都會執行到的程式碼(通常用於**資源釋放**) } ``` # 類別(Class)與物件(Object) ## 類別(Class) ●規格、藍圖 ●定義物件的抽象特徵,決定物件實體時的樣子 ## 物件(Object) ●依照類別,具體實現而成的**執行個體(instance)** ●狀態可以在執行階段被改變 ●實際配置記憶體 ## 宣告類別 使用class關鍵字 ```csharp= public class Student { 類別屬性 方法 欄位 } ``` ![](https://i.imgur.com/bCtSMu0.png) ## 建立物件 使用new關鍵字 ```csharp= using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ConsoleApp5.Models;//須寫出這行才能使用該資料夾的Class namespace ConsoleApp5 { class Program { static void Main(string[] args) { Student a = new Student();//使用new建立新的物件 Student b = new Student();//a與b各為獨立物件 Student c = a;//c與a指向同樣物件 } } } ``` ![](https://i.imgur.com/4x5mxJP.png) ## 建立類別屬性 **快捷鍵prop:** 自動產生類別屬性格式 **快捷鍵///:** 自動產生註解(可以明確表示屬性用途) get:可取得(預設public) set:可設定(預設public) ```csharp= public class Student { public Student() { this.ID = "A" + DateTime.Now.Millisecond; } /// <summary> /// 編號 /// </summary> public string ID { get; private set; }//private表示ID透過物件無法修改 /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 電話 /// </summary> public string Phone { get; set; } /// <summary> /// 電子郵件 /// </summary> public string Email { get; set; } } ``` ## 建構子(Constructor) **快捷鍵ctor:** 自動產生建構子格式 1.建構子名稱會與class名稱相同 2.可讓產生物件時先具有建構子內的設定 ※this關鍵字:類別內代稱物件用的字 ```csharp= public class Student { public Student() { this.ID = "A" + DateTime.Now.Millisecond; } /// <summary> /// 編號 /// </summary> public string ID { get; private set; }//private表示ID透過物件無法修改 /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 電話 /// </summary> public string Phone { get; set; } /// <summary> /// 電子郵件 /// </summary> public string Email { get; set; } } ``` ## get set舊式寫法 ```csharp= public class Student { private string _address;//先宣告私有變數 public string Address { get { return _address;//回傳變數內容 } set { _address = value;//將value存至宣告變數內 } } //以上等同於↓ public string address { get; private set; } //或可寫成↓ private string _address; public string Address { get => _address;//簡化get的寫法 set { _address = value; } } ``` 舊式寫法的用途(ex:須在set裡面判斷邏輯) 注意: 1.邏輯主要為簡單判斷,不應太複雜 2.邏輯寫在set裡面,不是在get裡面 ```csharp= public int age { get => _age; set { if (value < 0 || value > 150) { throw new ArgumentOutOfRangeException($"{nameof(value)} ....."); } _age = value; } } ``` ## 自動屬性 C# 3 增加了自動實作屬性(automatically implemented properties),或簡稱「自動屬性」。這個新語法能夠讓我們在定義屬性時省去宣告私有欄位的麻煩,當類別裡面的屬性數量很多的時候,這個語法可以讓程式碼簡潔許多。請看底下的範例: ```csharp= // C# 3:自動實作屬性(免寫私有欄位)。 public class Employee { public string ID { get; private set; } public string Name { get; set; } public DateTime Birthday { get; set; } // 想像底下還有一堆屬性,而我們能夠省下多少私有欄位的宣告。 public Employee(string id) // 建構子 { ID = id; } } ``` 請注意屬性 ID 可讓外界讀取,但是只有類別本身才能修改其值。所謂的類別本身,當然包含類別的建構子和方法。可是,如果我們希望這個唯讀屬性只允許在建構子中設定一次初始值,以後就再也不能修改了——即使在類別的其他方法中也不能修改,這種情況要怎麼寫呢? C# 5 沒辦法做到——除非使用唯讀的私有欄位,但程式碼寫起來比較囉嗦。 ## 唯讀自動屬性 到了 C# 6,終於在「不可改變性」(immutability)這方面做了改進,提供更好的寫法,也就是「唯讀自動屬性」(read-only automatically implemented properties)語法,像這樣: ```csharp= // C# 6:唯讀的自動屬性。 public class Employee { public string ID { get; } // 沒有 setter,這是個唯讀自動屬性。 public Employee(string id) { ID = id; // 設定唯讀自動屬性的初始值。 } private void ChangeID(int id) { ID = id; // 編譯失敗:ID 是唯讀屬性。 } } ``` 如您所見,此處有兩個重點: 1.屬性 ID 只有 getter,沒有 setter 了。它是個唯讀自動屬性。 2.由於 ID 是唯讀屬性,因此即使是類別自己也無法修改 ID 的值——建構子除外。 ## 練習-建立客戶類別 ```csharp= //(建立Customer類別並寫在其中) public class Customer { public Customer() { } public string Name { get; set; } public string Level { get; set; } public Int64 Money { get; set; } } ``` ```csharp= //(物件建立在主要執行Program中) static void Main(string[] args) { Customer A = new Customer(); A.Name = "Amy"; A.Level = "normal"; A.Money = 100; } ``` ## 建構子內設定參數 產生物件時可先具有建構子內的設定 →輸入參數代表:物件建立時應該需實際給予參數內容 ```csharp= class Student { public Student(string name, string email)//物件建立需給予姓名和email內容 { this.ID = "A" + DateTime.Now.Millisecond; this.Email = email;//參數內容會存取在物件屬性變數內 this.Name = name;//參數內容會存取在物件屬性變數內 } /// <summary> /// 編號 /// </summary> public string ID { get; private set; } /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 電話 /// </summary> public string Phone { get; set; } /// <summary> /// 電子郵件 /// </summary> public string Email { get; set; } } } ``` ↓類別建構子設定參數後,於主程式建立新物件時,需給予參數實際內容 ```csharp= static void Main(string[] args) { Student a = new Student("Amy", "123@gmail");//物件建立需給予姓名和email內容 Console.WriteLine(a.Name);//參數內容會存取在物件屬性變數內 Console.WriteLine(a.Email);//參數內容會存取在物件屬性變數內 Console.ReadLine(); ``` ![](https://i.imgur.com/n8LLZZP.png) ## 類別內建立方法 ex:停車免費時數計算 1.建立類別Customer(同上面建立客戶類別) ```csharp= //(建立Customer類別並寫在其中) public class Customer { public string Name { get; set; } public string Level { get; set; } public Int64 Money { get; set; } } ``` 2.於類別內建立方法:計算免費停車時數 ```csharp= public class Customer { public string Name { get; set; } public string Level { get; set; } public Int64 Money { get; set; } public int GetParkingTime()//建立方法(回傳int型別內容) { int times = 0;//預設可免費停車時數為0 Int64 Money = this.Money;//將物件消費的金額存入Money變數; switch (this.Level)//判斷內容為:物件的Level屬性內容 { case "VVIP": times = (int) Money / 3000 * 2;//強制將Int64轉為int if (times > 10) times = 10; break; case "VIP": times = (int) Money / 5000 * 2; if (times > 6) times = 6; break; case "normal": times = (int) Money / 5000 * 2; if (times > 2) times = 2; break; } return times; } ``` 3.於主程式內建立物件,並執行方法計算免費停車時數 ```csharp= class Program { static void Main(string[] args) { Customer A = new Customer(); A.Name = "Amy"; A.Level = "VVIP"; A.Money = 10000; var Times=A.GetParkingTime(); Console.WriteLine($"{A.Name}顧客您好,可免費停車時數為{Times}!!!"); Console.ReadLine(); } } ``` 輸出結果如下↓ ![](https://i.imgur.com/9B6nJV6.png) ## 建立兩個類別,並讓兩個類別內的物件可以互動 ex:建立學生類別與課程類別,並挑出成績低於70分的課程 1.建立課程的類別 ```csharp= public class Course { public string Name { get; set; } public int Score { get; set; } } ``` 2.建立學生的類別 ```csharp= public class Student2 { public Student2(string no,string name)//學生編號&姓名 { this.No = no; this.Name = name; this.MyCourse = new List<Course>(); //每次產生物件時,必須將List<T>初始化才能新增內容進去 } public string No { get; set; } public string Name { get; set; } public string Phone { get; set; } public List<Course> MyCourse { get; set; } //因為Course類別內有很多屬性,故使用List<T>型別儲存內容 } ``` 3.於主程式建立學生物件與課程物件,將學生物件的課程新增內容進去,並挑出低於70分項目 ```csharp= class Program { static void Main(string[] args) { Student2 A = new Student2("A001","Amy"); Course CA = new Course(); CA.Name = "C#"; CA.Score = 95; Course CB = new Course(); CB.Name = "db"; CB.Score = 65; A.MyCourse.Add(CA); A.MyCourse.Add(CB); foreach(var item in A.MyCourse) { if(item.Score<70) Console.WriteLine($"Name:{item.Name},Score:{item.Score}"); } Console.ReadLine(); } } ``` ## 回家練習-網頁購物車 Q:建立學員、課程、購物車的類別並使其互動,能看出學員購買的課程有哪些,並計算加總購物金額 ●自我試作↓ Student.cs ```csharp= public class Student { public Student(string id,string name) { this.ID = id; this.Name = name; this.MyCourse = new List<Course>(); } public string ID { get; set; } public string Name { get; set; } public List<Course> MyCourse { get; set; } public double BuyingCar() { double money = 0; foreach (var item in this.MyCourse) money += item.Price; if (money > 20000) { money *= 0.8; } else if (money > 10000) { money *= 0.9; } else money *= 0.8; return money; } ``` Course.cs ```csharp= public class Course { public string CourseName { get; set; } public int Price { get; set; } public Course(string coursename,int price) { this.CourseName = coursename; this.Price = price; } } ``` Program.cs ```csharp= public class Program { static void Main(string[] args) { Student A = new Student("A001", "Amy"); Course C1 = new Course("C#",1000); Course C2 = new Course("C++", 1800); Course C3 = new Course("JAVA", 1500); Course C4 = new Course("Python", 1200); Course C5 = new Course("DataBase", 2500); A.MyCourse.Add(C1); A.MyCourse.Add(C2); A.MyCourse.Add(C3); A.MyCourse.Add(C4); A.MyCourse.Add(C5); double TotalMoney= A.BuyingCar(); Console.WriteLine($"總金額為{TotalMoney}元"); Console.ReadLine(); } ``` ●參考答案↓(將購物車方法另外寫成一個類別) Student.cs ```csharp= public class Student { public Student(string id, string name) { this.ID = id; this.Name = name; this.MyCourse = new List<Course>(); } public string ID { get; set; } public string Name { get; set; } public List<Course> MyCourse { get; set; } } ``` Course.cs ```csharp= public class Course { public string CourseName { get; set; } public int Price { get; set; } public Course(string coursename, int price) { this.CourseName = coursename; this.Price = price; } } ``` BuyingCar.cs ```csharp= public class BuyingCar { public double MyBuyingCar(Student x) { double money = 0; foreach (var item in x.MyCourse) money += item.Price; if (money > 20000) { money *= 0.8; } else if (money > 10000) { money *= 0.9; } else money *= 0.8; return money; } } ``` Program.cs ```csharp= public class Program { static void Main(string[] args) { Student A = new Student("A001", "Amy"); Course C1 = new Course("C#", 1000); Course C2 = new Course("C++", 1800); Course C3 = new Course("JAVA", 1500); Course C4 = new Course("Python", 1200); Course C5 = new Course("DataBase", 2500); BuyingCar B1 = new BuyingCar(); A.MyCourse.Add(C1); A.MyCourse.Add(C2); A.MyCourse.Add(C3); A.MyCourse.Add(C4); A.MyCourse.Add(C5); double TotalMoney = B1.MyBuyingCar(A); Console.WriteLine($"總金額為{TotalMoney}元"); Console.ReadLine(); } } ```