📦 變數、型別與運算子
📌 什麼是變數?
變數就像一個有標籤的盒子,你可以在裡面放東西(資料),標籤就是變數名稱。
// 宣告變數的基本語法:型別 變數名稱 = 初始值;
int age = 25; // 一個裝整數的盒子,標籤叫 age
string name = "小明"; // 一個裝字串的盒子,標籤叫 name
double height = 175.5; // 一個裝小數的盒子,標籤叫 height
bool isAlive = true; // 一個裝布林值的盒子,標籤叫 isAlive
📌 值型別(Value Types)
值型別直接儲存資料本身,就像盒子裡直接放了東西。
// 整數型別
byte small = 255; // 0 到 255(1 byte = 8 位元)
short medium = 32767; // -32768 到 32767(2 bytes)
int normal = 2147483647; // 最常用的整數型別(4 bytes)
long big = 9223372036854775807L; // 超大整數,後面加 L(8 bytes)
// 浮點數型別
float price = 19.99f; // 單精度浮點數,後面加 f(4 bytes)
double pi = 3.14159265; // 雙精度浮點數,最常用的小數型別(8 bytes)
decimal money = 1000.50m; // 精確小數,適合金融計算,後面加 m(16 bytes)
// 布林型別
bool isOpen = true; // 只有 true(真)或 false(假)兩個值
// 字元型別
char grade = 'A'; // 用單引號,只能放一個字元
char heart = '♥'; // 也可以放 Unicode 字元
// 為什麼要用 decimal 而不是 double 算錢?
Console.WriteLine(0.1 + 0.2); // 輸出 0.30000000000000004(不精確!)
Console.WriteLine(0.1m + 0.2m); // 輸出 0.3(精確!)
📌 參考型別(Reference Types)
參考型別儲存的是資料的「地址」,就像盒子裡放了一張紙條,寫著東西放在哪裡。
// string 字串 — 最常用的參考型別
string greeting = "你好世界"; // 字串用雙引號包起來
string empty = ""; // 空字串(有盒子,但裡面沒東西)
string? nullable = null; // null 表示「沒有盒子」
// object — 所有型別的基底類別
object anything = 42; // 可以放任何東西
anything = "現在變成字串了"; // 可以改放不同型別的值
// dynamic — 動態型別(執行時才檢查型別)
dynamic flexible = 100; // 目前是整數
flexible = "hello"; // 現在變成字串(編譯時不會報錯)
// flexible.SomeMethod(); // 如果方法不存在,執行時才會錯
📌 var 關鍵字與型別推斷
// var 讓編譯器自動推斷變數的型別
var count = 10; // 編譯器推斷為 int
var message = "Hello"; // 編譯器推斷為 string
var ratio = 3.14; // 編譯器推斷為 double
var isReady = false; // 編譯器推斷為 bool
// ⚠️ 使用 var 的注意事項
// var 必須在宣告時就給初始值
// var x; // ❌ 錯誤!編譯器無法推斷型別
var x = 0; // ✅ 正確!
// var 不等於 dynamic,var 在編譯時就確定型別
var num = 10; // num 就是 int,不能再賦值為其他型別
// num = "hello"; // ❌ 錯誤!不能把字串賦值給 int
📌 算術運算子
int a = 10; // 宣告變數 a 並賦值 10
int b = 3; // 宣告變數 b 並賦值 3
// 基本運算
Console.WriteLine(a + b); // 加法:13
Console.WriteLine(a - b); // 減法:7
Console.WriteLine(a * b); // 乘法:30
Console.WriteLine(a / b); // 整數除法:3(小數部分被捨去!)
Console.WriteLine(a % b); // 取餘數(模運算):1
// 注意:整數除法會捨去小數
Console.WriteLine(10 / 3); // 輸出 3(不是 3.333...)
Console.WriteLine(10.0 / 3); // 輸出 3.333...(其中一個是小數就會保留)
// 遞增與遞減
int counter = 0; // 計數器初始值為 0
counter++; // counter 變成 1(等於 counter = counter + 1)
counter--; // counter 變回 0(等於 counter = counter - 1)
// 複合賦值運算子
int score = 100; // 分數初始值 100
score += 10; // score 變成 110(等於 score = score + 10)
score -= 20; // score 變成 90
score *= 2; // score 變成 180
score /= 3; // score 變成 60
📌 比較運算子
int x = 10; // 宣告變數
int y = 20; // 宣告變數
// 比較運算子回傳 bool(true 或 false)
Console.WriteLine(x == y); // 等於:false(10 不等於 20)
Console.WriteLine(x != y); // 不等於:true(10 確實不等於 20)
Console.WriteLine(x > y); // 大於:false
Console.WriteLine(x < y); // 小於:true
Console.WriteLine(x >= 10); // 大於等於:true
Console.WriteLine(x <= 5); // 小於等於:false
📌 邏輯運算子
bool sunny = true; // 今天是晴天
bool warm = false; // 今天不暖和
// && (AND)— 兩個都要 true 結果才是 true
Console.WriteLine(sunny && warm); // false(晴天但不暖和)
// || (OR)— 其中一個 true 結果就是 true
Console.WriteLine(sunny || warm); // true(至少是晴天)
// ! (NOT)— 反轉 true/false
Console.WriteLine(!sunny); // false(把 true 反轉成 false)
// 實際應用範例
int age = 20; // 年齡
bool hasLicense = true; // 有駕照
// 判斷是否可以開車:年滿 18 歲「且」有駕照
bool canDrive = age >= 18 && hasLicense; // true
Console.WriteLine($"可以開車:{canDrive}"); // 輸出:可以開車:True
📌 型別轉換
// 隱式轉換(小型別 → 大型別,安全不會遺失資料)
int smallNumber = 100; // int 佔 4 bytes
long bigNumber = smallNumber; // long 佔 8 bytes,自動轉換
float f = smallNumber; // float 佔 4 bytes,可以放 int
double d = f; // double 佔 8 bytes,可以放 float
// 顯式轉換(大型別 → 小型別,可能遺失資料,需要強制轉換)
double pi = 3.14159; // 雙精度浮點數
int rounded = (int)pi; // 強制轉換,小數部分被捨去 → 3
Console.WriteLine(rounded); // 輸出:3
long hugeValue = 999999999999L; // 超大的 long 值
int truncated = (int)hugeValue; // ⚠️ 溢位!結果不正確
// Convert 類別(最安全的轉換方式)
string numberText = "42"; // 這是字串 "42",不是數字
int number = Convert.ToInt32(numberText); // 轉換成整數 42
double dec = Convert.ToDouble("3.14"); // 轉換成浮點數 3.14
bool flag = Convert.ToBoolean("true"); // 轉換成布林值 true
string back = Convert.ToString(42); // 轉換回字串 "42"
// Parse 與 TryParse
int parsed = int.Parse("123"); // 轉換成功 → 123
// int fail = int.Parse("abc"); // ❌ 會拋出 FormatException
// TryParse 是最安全的方式(不會拋出例外)
if (int.TryParse("456", out int result)) // 嘗試轉換
{
Console.WriteLine($"轉換成功:{result}"); // 輸出:轉換成功:456
}
else
{
Console.WriteLine("轉換失敗"); // 如果輸入不是數字
}
📌 常數與唯讀
// const — 編譯時常數(值在編譯時就確定,永遠不能改變)
const double PI = 3.14159265358979; // 圓周率是不變的
const int MAX_SCORE = 100; // 最高分是固定的
// PI = 3.14; // ❌ 錯誤!常數不能修改
// readonly — 執行時常數(可以在建構子中設定值)
// readonly 只能用在類別的欄位,不能用在方法內
🤔 我這樣寫為什麼會錯?
❌ 錯誤:整數除法的陷阱
// ❌ 期望得到 3.333... 但得到 3
int a = 10; // 整數
int b = 3; // 整數
var result = a / b; // 整數 / 整數 = 整數(小數被捨去)
// result 是 3,不是 3.333...
// ✅ 正確:至少一個運算元要是小數型別
double result2 = (double)a / b; // 先把 a 轉成 double 再除
// result2 是 3.333...
❌ 錯誤:float 和 decimal 忘記加後綴
// ❌ 錯誤:
// float price = 19.99; // 錯誤!19.99 預設是 double
// decimal money = 100.50; // 錯誤!100.50 預設是 double
// ✅ 正確:
float price = 19.99f; // 加上 f 後綴
decimal money = 100.50m; // 加上 m 後綴
❌ 錯誤:null 與空字串搞混
string? a = null; // a 沒有指向任何東西(沒有盒子)
string b = ""; // b 指向一個空字串(有盒子,但裡面是空的)
// a.Length // ❌ NullReferenceException!null 沒有 Length
Console.WriteLine(b.Length); // ✅ 輸出 0(空字串長度為 0)
// 安全存取:用 ?. 運算子
Console.WriteLine(a?.Length); // 輸出 null(不會報錯)
❌ 錯誤:== 和 = 搞混
int x = 10; // = 是「賦值」(把 10 放進 x)
// if (x = 5) // ❌ 錯誤!這是賦值,不是比較
if (x == 5) // ✅ 正確!== 是「比較」(x 是否等於 5)
{
Console.WriteLine("x 等於 5");
}