C# == # 一、基本概念 > 參考連結: > youtube: [我阿嬤都會:C# 3小時初學者教學](https://youtu.be/T9BeejD3i0g?si=bZupLNX3MFRhQMNx) > ## 1. 開發環境、第一支程式 **Visual Studio Community** **Visual Studio for Mac** ### 第一支程式 - 副檔名為cs的檔案就是一支 C# 檔案 - C# 內每一個指令結束都要加上; ## 2. 常見資料型態&變數 ### 資料型態 - **字串 string** - 用於表達一段純文字 - 使用雙引號 - **在 C# 中,字串是不可變的** ```C# "你好" "Hello,World!" ``` - **字元 char** - 用於表達一個純文字 - 可以使用單引號 ```C# 'a' //屬於字元 'aa' //非字元 ``` - **整數 int** ```C# int 40 int -40 ``` - **浮點數 float vs double** ```C# double 160.5 float 160.5f ``` - 如果要指 float ,通常後面會加上 f,編譯器才知道這個數值為浮點數,否則預設數字字面量會是 double - double 較精確,範圍較大,但需要的內存容量也較多 - float 約有 7 位有效數字,精確度較低,但內存容量也較小,有時候 float 可能比 double 更快 - **布林值 bool** - true/false ### 變數 > C# 是一種 **靜態類型** 的程式語言,變量的類型在編譯的時候就已經確定了,可以更好的捕捉類型錯誤 > 就算是以 `var` 宣告,但編譯器會依據上下文去做型別的推斷,當型別已被推斷出來,後續就不能更改,如 var number = 10 ,後續就不能再將 "hello" 賦值給 number - 變數就像是一個容器,可以將不同的資料型態放入這個容器中 - 可以更方便來管理這些資料(資料內容改變時不需每行調整) - **創建變數** - C# 有**型別宣告(Type Declaration)**,在變數前放上資料型別,可以讓編輯器知道如何處理這個變數 - 變數名稱相同會發生衝突 ```C# string name = "小黃"; char sex = "M"; int age = "20"; double height = 180.3; bool is_male = true; ``` - **重新賦值** - 可以重新賦值但不能重新做型別宣告 ```C# string name = "小黑"; Console.Writeline("我的小狗叫"+name); name = "小黃"; Console.Writeline("我的小狗叫"+name); int name = 30; // 錯誤:name已經被定義 Console.Writeline("我的小狗叫"+name); ``` <font color="#CB4042"> </font> ### 運算子 - `==` 和 `!=` - 在運算中,不同類型的值無法直接做比較,需將左右兩側的值轉為相同資料類型才能做比較 - 可以使用 `Equal` 替代: - `x.Equals(0)` 用於比較 x 是否等於 0。在這種情況下,x 必須是可以與 0 進行比較的類型,例如 int、double 或 decimal - 在 C# 中,`false` 被視為 0,`true` 被視為 1,所以在此情形下布林值可以和數值比較 - ## 3. 字串常見用法 ### 換行 \n ```csharp Console.WriteLine("Hello \nMr.White"); Hello Mr.White ``` ### 反斜線的使用 \ - 告訴程式語言反斜線後的內容無用途 ```csharp Console.WriteLine("Hello\" Mr.White"); Hello" Mr.White ``` ### 字串相加 + ```csharp string phrase = "Hello"; Console.WriteLine(phrase + "Mr.White"); Hello Mr.White ``` #### 或使用插值字串 $ : ```csharp string phrase = "Hello"; Console.WriteLine($"{phrase} Mr.White"); Hello Mr.White ``` ### 字串長度 .Length ```csharp string phrase = "Hello Mr.White"; Console.WriteLine( phrase.Length ); Console.WriteLine( "Hello Mr.White".Length ); 14 ``` ### 其他字串方法 - **.ToUpper()** ```csharp string phrase = "Hello Mr.White"; Console.WriteLine( phrase.ToUpper() ); HELLO MR.WHITE ``` - **.ToLower()** ```csharp string phrase = "Hello Mr.White"; Console.WriteLine( phrase.ToLower() ); hello mr.white ``` - **.Contains()** - 字串內是否包含某些子字串 - ()內需放上參數(子字串) ```csharp string phrase = "Hello Mr.White"; Console.WriteLine( phrase.Contains("Hello") ); True ``` - **索引值 []** - 用位置去找值 - C#索引值一樣是從0開始 ```csharp string phrase = "Hello Mr.White"; Console.WriteLine( phrase[1] ); e ``` - **.IndexOf()** - 用值去找位置 - ()內需放上參數(字串) - 如果有重複字元,會回傳最前面的索引值 - 當查詢的字串或字元不存在時,則會回傳-1 ```csharp string phrase = "Hello Mr.White"; Console.WriteLine( phrase.IndexOf('H') ); Console.WriteLine( phrase.IndexOf('l') ); Console.WriteLine( phrase.IndexOf("White") ); Console.WriteLine( phrase.IndexOf("qq") ); 0 2 9 -1 ``` - **.Substring()** - 用於切割字串 - ()內需放上參數(切割位置的索引值,切割的字元個數) - C# 中的字串是不可變的, ```C# string phrase = "Hello Mr.White"; Console.WriteLine( phrase.Substring(2) ); Console.WriteLine( phrase.Substring(6,3) ); llo Mr.White Mr. ``` ## 4. 數字常見用法(整數、浮點數) ### 加減乘除 - 除法:如果兩個數值都是整數,則得到的結果也會是整數(小數部分直接被截斷) - 先乘除後加減 ```C# Console.WriteLine(5+2); // 7 Console.WriteLine(5-2); // 3 Console.WriteLine(5*2); // 10 Console.WriteLine(5/2); // 2 Console.WriteLine(10/3); // 3 Console.WriteLine(5/2.0); // 2.5 ``` ```C# Console.WriteLine(5+2*3); // 11 Console.WriteLine((5+2)*3); // 21 int num = 3; Console.WriteLine((5+2)*num); // 21 ``` ### 常用數學方法 Math - **絕對值 Math.Abs()** - ()內放要做絕對值的數 ```C# Console.WriteLine(Math.Abs(-10)); Console.WriteLine(Math.Abs(10)); 10 ``` - **次方 Math.Pow()** - ()內需放上兩個參數(底數,指數) ```C# Console.WriteLine(Math.Pow(2, 3)); Console.WriteLine(Math.Pow(2, 5)); 8 32 ``` - **開根號 Math.Sqrt()** - ()內需放上參數 ```C# Console.WriteLine(Math.Sqrt(64)); 8 ``` - **最大值 Math.Max()** - ()內需放上兩個數值作比較 ```C# Console.WriteLine(Math.Max(2, 100)); 100 ``` - **最小值 Math.Min()** - ()內需放上兩個數值作比較 ```C# Console.WriteLine(Math.Min(2, 100)); 2 ``` - **四捨五入 Math.Round()** - ()內需放上數值 ```C# Console.WriteLine(Math.Round(3.5)); Console.WriteLine(Math.Round(8.2)); 4 8 ``` ## 5. 取得用戶輸入 Console.ReadLine() > Console.ReadLine() 從控制台讀取輸入的值或字串 > Console.WriteLine() 輸出值或字串至控制台 > WriteLine()會自動做換行,如果希望不要換行,則可用Write() - Console.ReadLine() 會預設將取得的資料當作字串的資料型態 ```C# Console.WriteLine("請輸入您的名字:"); string name = Console.ReadLine(); Console.WriteLine("嗨~" + name); ``` ## 6. 建立基本計算機 > 取得用戶輸入的數字,相加後回傳給用戶 - **資料型別轉換:字串轉數字** - Convert.ToInt32() - ToInt32()因為是將字串轉為整數,所以如果輸入的值為浮點數則會出錯 - Convert.ToDouble() >[!Tip] **Convert.ToInt** vs **int.Parse** >- 兩者都可以將字串轉為數字,但 `int.Parse` 較為嚴謹,如果無法轉為數字,會得到 `FormatException` (格式不正確),如果字串是 null ,會得到 `ArgumentNullException` >- `Convert.ToInt` 則較寬容,**可以接受多種類型的參數**,包括 null。如果參數為 null,則返回 0。當字串無法轉換為整數時,會拋出 `FormatException`,但對於其他類型(如布林值),則會進行相應的轉換。 ```C# Console.Write("請輸入第一個數:"); double num1 = Convert.ToDouble(Console.ReadLine()); Console.Write("請輸入第二個數:"); double num2 = Convert.ToDouble(Console.ReadLine()); Console.WriteLine(num1+num2); ``` ## 7. 陣列 Array - 大型容器,裡面可存放多個資料 - 陣列內存放的都是同類型的資料型態 - C# 的陣列是一種固定大小的資料結構,所以不像python一樣可以用push/pop/shift/unshift等動態操作改變陣列大小 - 如果要調整大小,則需透過++創建一個新的陣列++,將現有陣列複製到新陣列中,或是使用++List<T>的資料結構++ ### 創建陣列 ```C# int[] scores = {50, 60, 85, 65}; Console.WriteLine(scores[0]); // 50 Console.WriteLine(scores[1]); // 60 Console.WriteLine(scores[2]); // 85 ``` ### 改變陣列內的值 ```C# int[] scores = {50, 60, 85, 65}; scores[0] = 62; Console.WriteLine(scores[0]); // 62 Console.WriteLine(scores[1]); // 60 Console.WriteLine(scores[2]); // 85 ``` ### 創建陣列的另一種方法 > 在創建的當下還不知道要存放的值 ```C# string[] phones = new string[10]; //表示這個陣列內可以放10個值 phones[0]="0123456789"; phones[1]="0123456788"; phones[2]="9876543210"; Console.WriteLine(phones[0]); // 0123456789 Console.WriteLine(phones[1]); // 0123456788 Console.WriteLine(phones[2]); // 9876543210 ``` ### <font color="#CB4042">如何印出整個陣列</font> > 當使用 Console.WriteLine(phones) 來打印陣列時,不會印出整個陣列的值,而是會輸出陣列的物件名稱,如 `System.String[]`。這是因為 `Console.WriteLine` 方法不會自動處理陣列的內容。 - **使用 foreach 迴圈** ```csharp foreach (var phone in phones) { Console.WriteLine(phone); } ``` - **使用 for 迴圈** ```csharp for (int i = 0; i < phones.Length; i++) { Console.WriteLine(phones[i]); } ``` - **使用 string.Join 方法** - 這樣會將所有的元素用連接符號 `","` 連接成一個字串並打印出來 ```csharp Console.WriteLine(string.Join(", ", phones)); ``` ## 8. if 判斷句 - if / else / else if ```csharp // 1. 如果我肚子餓,我就去吃飯 bool hungry = true; if(hungry) { System.Console.WriteLine("我就去吃飯"); } // 2. 如果今天有下雨,我就開車去上班;否則,我就走路去上班 bool rainy = true if (rainy) { System.Console.WriteLine("我就開車去上班"); } else { System.Console.WriteLine("我就走路去上班"); } // 3. 如果你考100分,我給你1000元;或是如果你考80分以上,我給你500元;或是如果你考60分以上,我給你100元;否則,你給我300元 int score = 100; if (score == 100) { System.Console.WriteLine("我給你1000元"); } else if(score >= 80) { System.Console.WriteLine("我給你500元"); } else if(score >= 60) { System.Console.WriteLine("我給你100元"); } else { System.Console.WriteLine("你給我300元"); } // 4. 如果你考100分,且今天有下雨,我給你1000元;否則,你給我100元 int score = 100; bool rainy = true; if(score == 100 && rainy) { System.Console.WriteLine("我給你1000元"); } else { System.Console.WriteLine("你給我100元"); } // 5. 如果你考100分,或今天有下雨,我給你1000元;否則,你給我100元 int score = 100; bool rainy = false; if(score == 100 || rainy) { System.Console.WriteLine("我給你1000元"); } else { System.Console.WriteLine("你給我100元"); } ``` ## 9. 進階計算機 > 取得用戶輸入的兩個數值及一個運算符號做加減乘除運算 ```csharp System.Console.Write("請輸入第一個數: "); double num1 = System.Convert.ToDouble(System.Console.ReadLine()); System.Console.Write("請輸入要做的運算: "); string oper = System.Console.ReadLine(); System.Console.Write("請輸入第二個數: "); double num2 = System.Convert.ToDouble(System.Console.ReadLine()); if (oper == "+") { System.Console.WriteLine($"{num1} + {num2} = " + (num1 + num2)); } else if (oper == "-") { System.Console.WriteLine($"{num1} - {num2} = " + (num1 - num2)); } else if (oper == "*") { System.Console.WriteLine($"{num1} * {num2} = " + (num1 * num2)); } else if (oper == "/") { System.Console.WriteLine($"{num1} / {num2} = " + (num1 / num2)); } else { System.Console.WriteLine("不合法的運算符號"); } ``` ## 10. while迴圈 - 寫法一: 先判斷條件再執行 ```csharp int num = 1; //初始值 while (num<=5) // 條件 { System.Console.WriteLine(num); num = num + 1; // 改變循環條件 } ``` - 寫法二: 先執行後再判斷條件(至少執行一次) ```csharp int num = 6; do { System.Console.WriteLine(num); num = num + 1; // 改變循環條件 } while (num<=5) ; ``` ## 11. 猜數字遊戲 > 先設定謎底,可以無限次數讓玩家猜測 > 如果玩家猜測的數字比謎底小,會提示要大一點 > 如果玩家猜測的數字比謎底大,會提示要小一點 > 如果玩家猜測出正確數字,則會告知回答正確 - 以下寫法是參考自 書籍 ASP.NET CORE MVC ```csharp Random random = new Random(); int secretNum = random.Next(1,101); int attempts = 0; bool correctguess = false; Console.WriteLine("歡迎來到猜數字遊戲!"); while(!correctguess) { Console.Write("請從 1-100 中輸入一個整數值: "); string input = Console.ReadLine(); if(!int.TryParse(input, out int guess)) { Console.WriteLine("輸入無效,請重新輸入"); continue; } attempts++; if(guess < secretNum) { Console.WriteLine("比這個數字大喔!再猜一次吧!"); } else if(guess > secretNum) { Console.WriteLine("比這個數字小喔!再猜一次吧!"); } else { if (attempts <= 4) {Console.WriteLine($"恭喜你答對啦~正確答案就是 {secretNum} !你總共猜了 {attempts} 次,超級厲害!"); } else if (attempts <= 8) {Console.WriteLine($"恭喜你答對啦~正確答案就是 {secretNum} !你總共猜了 {attempts} 次,很不錯唷!"); } else {Console.WriteLine($"恭喜你答對啦~正確答案就是 {secretNum} !你總共猜了 {attempts} 次,下次繼續加油!"); } correctguess = true; } } Console.ReadLine(); ``` ## 12. for迴圈 > 適用在已知迭代次數的情況 ```csharp for(int i = 1; i <= 5; i++) { System.Console.WriteLine(i); } ``` - 可以用來讀取陣列內所有的值 ```csharp int[] nums = { 10,20,37,40,5 }; for(int i = 0; i< nums.Length; i++) { System.Console.WriteLine(nums[i]) } ``` ## 13. 二維陣列 > 在陣列中再放陣列 - 可以更方便去存取資料、記錄資訊 - 一樣要先指定陣列中存放資料的型態,然後再陣列的中括號內再放上`,`表示這個是二維陣列 ```csharp int[,] nums = { {1,2,3}, {4,5,6}, {7,8,9} }; // 取得二維陣列內的資料 System.Console.WriteLine(nums[0,0]); // 1 System.Console.WriteLine(nums[1,2]); // 6 System.Console.WriteLine(nums[2,1]); // 8 ``` ### 創建空的二維陣列 ```csharp // 創建一個空的二維陣列,有三個橫排(row),四個直排(col) int[,] num = new int[3,4]; // 放入資料 num[0, 0] = 3; num[0, 1] = 10; num[0, 2] = 5; num[0, 3] = 11; ``` - 三維陣列 `int[,,]` 以此類推 ## 14. class & object ### 類別 class - 很多東西無法用單一資料型態來做表示,因為該物件可能有多種屬性、行為 - 可以透過創建 class類別 ,建立一種屬於自己的++資料型態的模板++ - 類別的命名:第一個字母通常會以大寫表示 ex: Person.cs ```csharp using System; using System.Collections.Generic; using System.Ling; using System.Text; using System.Threading.Tasks; namespace Animal { class Person { # 寫上類別屬性 public double height; public int age; public string name; } } ``` ### 物件 object:以類別(模板)創建的實體 - 模板名稱 變數名稱 = new 模板名稱(); ```csharp Person person1 = new Person(); person1.height = 170.5; person1.age = 42; person1.name = "小黑"; System.Console.WriteLine(person1.height); ``` > 物件是基於類別而創建的實例,而通常物件會包含屬性和方法 ## 15. namespace & using ### namespace 命名空間 - 可以用來存放、分類和管理模板(class)的空間 - 只要在namespace範圍之外的地方使用到裡面的模板,則需要加上namespace名稱,例: ```csharp Person person1 = new Person(); // 寫為 Animal.Person person1 = new Person(); ``` ### using 使用 > 類似 import 用法(?) - using namespace名稱 - 這個寫法就可以在使用模板時不用額外加上namespace名稱 ```csharp using Animal; Person person1 = new Person(); ``` - namespace 內可以再放上 namespace - `using namespace1.namespace2;` - 意思是要去使用 namespace1 空間內的 namespace2 空間模板 ## 16. method 方法 > 類似 function / def 用法(?) - 用於在 class 裡面定義功能來做使用 ```csharp // Person.cs namespace Animal { class Person { public double height; public int age; public string name; public void SayHi() { Console.WriteLine("Hi~我是" + name); } public void IsAdult() { if (age >= 18) { Console.WriteLine(true); } else { Console.WriteLine(false); } } } } // Program.cs Person person1 = new Person(); person1.name = "安安"; person1.age = 14; person1.SayHi(); // 用 person1 物件去呼叫 SayHi 方法 person1.IsAdult(); // 用 person1 物件去呼叫 IsAdult 方法 ``` - `void` 是指不回傳值,所以 void 定義的方法內如果寫到 return 則會出錯 - 如果要在方法內回傳值到呼叫的地方,則將 void 改為要回傳的資料型態,如 bool / int / string 等 - 如果在 method 內做 ==return,等於覆蓋原本的呼叫== ```csharp public bool IsAdult() { return true } // 則在使用時 person.IsAdult() ==> 這個等同於 true ``` - 把結果回傳至呼叫的地方,後續才能繼續做處理 ### method 設計也可以放入參數 ```csharp public int Add(int num1, int num2, string qq) { return num1 + num2; } // 呼叫時:Console.WriteLine(person.Add(3,9,"happy")) ``` - 參數可以是不同資料型態 - 如果呼叫時缺少引數也會出錯(除非參數有預設值) ## 17. Main 方法 - 程式碼開始執行的地方,也是**專案的進入點** - 特別的方法,不需額外呼叫 - C# 會幫我們將 Program.cs 的內容包裝成一個 Main 方法 - 實際上的運作原理,會是 class Program 裡面再包了一個 main 方法: ```csharp class Program { static void Main() { Console.WriteLine("妳好~") } } ``` - 我們新增專案時,預設的 Program.cs 就是上面這個架構 - Main 方法通常是 **static** 的,資料型態有 **void / int**,可以有以下幾種形式: ```csharp static void Main() { // 程式的程式碼 } static void Main(string[] args) { // 程式的程式碼 } static int Main() { // 程式的程式碼 return 0; // 返回一個整數狀態碼 } static int Main(string[] args) { // 程式的程式碼 return 0; // 返回一個整數狀態碼 } ``` 1. **static void Main()** - 這是一個靜態的 Main 方法,沒有參數,返回類型為 void。 2. **static void Main(string[] args)** - 這是一個靜態的 Main 方法,接受一個字串陣列作為參數,返回類型為 void。 3. **static int Main()** - 這是一個靜態的 Main 方法,沒有參數,返回類型為 int,可以返回一個整數狀態碼。 4. **static int Main(string[] args)** - 這是一個靜態的 Main 方法,接受一個字串陣列作為參數,返回類型為 int,可以返回一個整數狀態碼。 ## 18. Constructor 建構方法 ```csharp class Person { public double height; public int age; public string name; public Person() { Console.WriteLine("創建成功") } // 當 Person person1 = new Person() 被執行時,就會在console印出"創建成功" } ``` - 特別方法,每當物件被創建時,都會被呼叫一次的方法 - 和 class 同名 - 不需寫上回傳的資料型態 ex **void int string** - 因為<font color="#CB4042">**建構方法不能回傳資料**</font> - 建構方法也可以寫入想傳入的參數 `public Person(string qq){}` - 所以可以將每個物件都有的屬性(創建時需設置的屬性或方法),放入建構方法的參數內 ```csharp // 寫法一 public Person(double h, int a, string n) { height = h; age = a; name = n; } // 寫法二 public Person(double height, int age, string name) { this.height = height; this.age = age; this.name = name; } // Person person1 = new Person(170.5, 42, "小黑"); // Person person2 = new Person(158.9, 12, "小黃"); ``` - 如果參數和原本設的屬性同名,則需加上this,才能讓程式去區分哪個是屬性,哪個表示參數 ## 19. getter & setter > 補充:存取修飾詞- public / private ... - 用來**限制屬性的存取**: 1. 存取修飾詞設為 private 2. 使用 get / set 做回傳值的設定 3. 更新建構方法的屬性 - private 設定的屬性,只能在這個 class 裡面做存取 - protected 設定的屬性,可以在這個類別以及子類別中做存取 ```csharp # Video.cs using System; namespace APP { public class Video { public string title; public string author; // 想限制 type 只有四種類型:教育、娛樂、音樂、其他 private string type; public Video(string title, string author, string type) { this.title = title; this.author = author; Type = type; } public string Type { get{ return type;} set { if(value == "教育" || value == "娛樂" || value == "音樂" || value == "其他") { type = value; } else { type = "其他" } } } } } ``` - get 作用: 當有人要取得 Type 時,要做的事情 - set 作用: 當有人要設定 Type 時,所設定的值,且可以用來做驗證 - 當有人要從外面寫入Type時`video.Type="音樂"`,會先透過set來做驗證、判斷 - 這段設定了當要從外面訪問 類型屬性 時,需要透過 Type 而不是 type ## 20. static attribute ### 靜態屬性:屬於類別本身的屬性 - 靜態屬性 vs 物件屬性 ```csharp # Video.cs using System; namespace APP { public class Video { public string title; public string author; private string type; public static int video_count = 0; public Video(string title, string author, string type) { this.title = title; this.author = author; this.type = type; video_count++; } } } ``` 1. 靜態屬性:`Video.video_count` - 靜態屬性屬於類別本身,所以需要Video.來使用 2. 物件屬性:`video1.Type`、`video1.author` - 如果要以物件去取得靜態屬性,可以另外寫一個方法來做使用 ```csharp public int getVideoCount() { return video_count; } ``` `video1.getVideoCount();` ## 21. static method & static class ### 靜態方法 - 這個方法屬於類別本身 - 可以直接被調用,不用額外創建物件 ```csharp #Tool.cs class Tool { public static void SayHi() { Console.WriteLine("Hello"); } } #Program.cs Tool.SayHi(); // 不用創建 tool 物件就能使用 ``` ### 靜態類別 - 無法以 new 去創建物件,因為靜態類別只是一種工具 - 靜態類別內只會包含靜態方法、靜態屬性、靜態字段 ## 22. 繼承 ```csharp # Person.cs class Person { public string name; public int age; public Person(string name, int age) { this.name = name; this.age = age; } public void PringAge() { Console.WriteLine(this.age); } public void PringName() { Console.WriteLine(this.name); } } # Student.cs class Student : Person { public string school; public Student(string name, int age, string school) { this.name = name; this.age = age; this.school = school; } public void PrintSchool() { Console.WriteLine(this.school); } } ``` - 因 Student 類別去繼承了 Person 類別,所以 Person 有的屬性 Student 也都有 - 此時如果去創建一個 student1 物件,則也可以調用 name / age 屬性、printAge / printName 方法 `Console.WriteLine(student1.name);` `student1.PrintAge();` --- # 二、自行補充 ## 1. 接口、類別、實例 - 接口 (Interface) 定義了一組方法和屬性,但沒有具體的實現方式,所以接口本身無法被實例化,**需要透過類別來做具體的實現**。 - 所以++類別中一定要有所有接口內定義的屬性及方法++,否則編譯器會報錯。 - 一個類別可以實現多個介面,達成類似多重繼承的效果(但 C# 本身沒有多重繼承的概念) - **類別創建實例,實例被創建時會自動擁有類別的屬性和方法**,所以不需額外實現這些方法和屬性。 - 實例透過類別被創建時,可以透過賦值給一變數,並++設定變數的類型為 **接口** 或者 **類別**++。 - 變數類型為接口的實例,只能使用接口中定義的方法,不能使用類別中額外定義的屬性或方法。 - 變數類型為類別的實例,可以使用接口中定義的方法,以及類別中額外定義的方法。 ```csharp // 定義接口 public interface IMyService { void DoWork(); } // 實現接口的類別 public class MyService : IMyService { public void DoWork() { Console.WriteLine("Doing work in MyService..."); } } class Program { static void Main(string[] args) { // 使用 MyService 類別創建實例 MyService myServiceInstance = new MyService(); myServiceInstance.DoWork(); // 調用 MyService 的方法 // 使用接口類型創建實例 IMyService myService = new MyService(); // 接口變數引用實現了接口的類別 myService.DoWork(); // 調用接口方法 } } ``` ## 2. 物件導向 ### 類別和物件 - 類別:類別是物件的藍圖或模板,定義了物件的屬性和行為。 - 物件:物件是類別的實例。每個物件都有自己的屬性值和功能。 ### 繼承 - 繼承允許一個 **類別** 從另一 **類別** 中去繼承屬性和方法。 - 子類別會自動有上層類別的屬性及方法,可以選擇是否要覆寫。 - C# 本身沒有多重繼承(一個類別同時繼承自多個類別)的概念。 ### 封裝 - ++封裝是將數據和行為封裝在類別內部,並限制外部訪問++。這通常通過訪問修飾符 (private/protected) 來實現 - **private**:用來限制屬性和方法只能在定義該成員的內部類別中使用,不能在外部或者子類別中被調用。 - **protected**:可以在定義該成員的類別內部和所有派生類別(子類別)中訪問,但無法在其他類別中訪問。 - 通過封裝,可以隱藏內部數據,控制對數據的訪問,並提高代碼的可維護性和安全性。 ### 多型 - 多型允許不同類型的物件以相同的方式進行操作。這可以通過 **方法重載** 和 **方法覆寫** 來實現。 - **靜態多型**:通常通過方法重載(Method Overloading)和運算符重載(Operator Overloading)來實現。 - 方法重載:++在同一個類別中++,定義多個名稱相同但參數列表不同的方法。 ```csharp public class MathOperations { public int Add(int a, int b) { return a + b; } public double Add(double a, double b) { return a + b; } public int Add(int a, int b, int c) { return a + b + c; } } ``` - **動態多型**:通常通過方法覆寫(Method Overriding)來實現。 - 方法覆寫:在子類別中重新定義父類別的方法。這樣在運行時,會根據對象的實際類型來調用相應的方法。 ```csharp public class Animal { public virtual void Speak() { Console.WriteLine("Animal speaks"); } } public class Dog : Animal { public override void Speak() { Console.WriteLine("Woof!"); } } public class Cat : Animal { public override void Speak() { Console.WriteLine("Meow!"); } } // 使用多型 Animal myAnimal; myAnimal = new Dog(); myAnimal.Speak(); // 輸出: Woof! myAnimal = new Cat(); myAnimal.Speak(); // 輸出: Meow! ``` ### 抽象 - 抽象類別和介面用於定義一組方法,這些方法必須在派生類別中實現。 - 抽象類別是一種++不能被實例化的類別++,通常用於作為其他類別的基類。**它可以包含抽象方法(沒有實作)和具體方法(有實作)。** - 抽象類別可以被其他類別繼承,這些子類必須實現抽象類別中的所有抽象方法。 - 抽象類別可以用作變數的型別,這使得可以使用多型性來處理不同的具體實例。 > 派生類別:子類別 ### 介面 - 介面定義了類別必須實現的方法,但不提供具體實現。這有助於實現多重繼承的效果。 ## 3. 基本計算題 ### 反轉字串 ```csharp using System; class Program { static void Main() { Console.Write("請輸入一個字串:"); string input = Console.ReadLine(); // 反轉字串 char[] charArray = input.ToCharArray(); // 將字串轉換為字符陣列 Array.Reverse(charArray); // 反轉字符陣列 string reversedString = new string(charArray); // 將反轉的字符陣列轉換回字串 Console.WriteLine($"反轉後的字串是:{reversedString}"); } } ``` - C# 中字串是不可變的,所以不能直接使用 input.Reverse(),要先轉為字符陣列 ### 計算平均數 ```csharp using System; class Program { static void Main() { int sum = 0; // 初始化總和 int count = 5; // 設定要輸入的整數數量 for (int i = 1; i <= count; i++) { Console.Write($"請輸入第 {i} 個整數:"); int number = Convert.ToInt32(Console.ReadLine()); sum += number; // 累加總和 } double average = (double)sum / count; // 計算平均數 Console.WriteLine($"五個整數的平均數是:{average}"); } } ``` - 利用 for 迴圈讓使用者輸入值 - 計算平均時,如果只用`sum/count`,則會使用整數除法,自動截斷小數位,所以需用 `double(sum)/count` 強制轉為 double