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