這四個技巧看似基礎,卻是寫出可維護、可擴充、高效能 C# 代碼的基石。本文深入實務場景說明每個技巧的「為什麼」與「怎麼做」。
依賴抽象 (介面) 而非具體實作。讓高層模組不依賴低層模組,雙方依賴抽象 (DIP)。
// ❌ 差:鎖定具體類型,無法傳入 List、Array、HashSet、DbQuery 結果
public void LoadItems(List items, ListBox listBox) { ... }
// ✅ 好:接受任何實作 ICollection 的類型
public void LoadItems(ICollection items, ListBox listBox)
{
foreach (var item in items) listBox.Items.Add(item);
}
// ✅ 更好:IEnumerable (僅需逐一查看),或 IReadOnlyCollection (需 Count)
public void LoadItems(IEnumerable items, ListBox listBox) { ... }
// ❌ 緊耦合:內部 new 具體類別,難以測試、替換
public class OrderService
{
private readonly SqlRepository _repo = new SqlRepository();
}
// ✅ 鬆耦合:依賴介面,由容器/呼叫端提供實作
public class OrderService
{
private readonly IRepository _repo;
public OrderService(IRepository repo) => _repo = repo;
}
// ❌ 胖介面:實作類別被迫實作不需要的方法
public interface IUserService
{
void CreateUser(User u);
void DeleteUser(int id);
void SendEmail(string to, string body); // 非用戶管理職責
void GenerateReport(); // 非用戶管理職責
}
// ✅ 拆分介面
public interface IUserRepository { void Create(User u); void Delete(int id); }
public interface IEmailService { void Send(string to, string body); }
public interface IReportGenerator { Report Generate(); }
// 自動實作屬性 (C# 3+)
public string Name { get; set; }
// 唯讀屬性 (C# 6+)
public DateTime CreatedAt { get; } = DateTime.UtcNow;
// 初始化後不可變 (C# 9+ init)
public class UserDto
{
public string Email { get; init; }
public string Name { get; init; }
}
// 完整封裝:驗證 + 通知
private string _email;
public string Email
{
get => _email;
set
{
if (value == _email) return;
if (!value.Contains("@")) throw new ArgumentException("Invalid email");
_email = value;
OnPropertyChanged(nameof(Email)); // INotifyPropertyChanged
}
}
public class ApiModel
{
[JsonPropertyName("user_id")] // System.Text.Json
[XmlAttribute("id")] // XmlSerializer
public int Id { get; set; }
[JsonIgnore] // 不序列化
[XmlIgnore]
public string InternalToken { get; set; }
[JsonInclude] // 私有屬性也序列化
private string CacheKey { get; set; }
}
public class KeyboardProcessor
{
// 事件:多點傳送,鬆耦合
public event Action LineReceived;
public void Run()
{
while (true)
{
string line = Console.ReadLine();
if (string.IsNullOrEmpty(line)) break;
// 執行緒安全觸發
LineReceived?.Invoke(line);
}
}
}
// 訂閱者
var processor = new KeyboardProcessor();
processor.LineReceived += line => Console.WriteLine($"Echo: {line}");
processor.LineReceived += line => File.AppendAllLines("log.txt", new[] { line });
processor.Run();
public interface IDiscountStrategy
{
decimal Calculate(Order order);
}
public class OrderService
{
private readonly IDiscountStrategy _strategy;
public OrderService(IDiscountStrategy strategy) => _strategy = strategy;
public decimal GetFinalPrice(Order order) => order.Total - _strategy.Calculate(order);
}
// 具體策略
public class VipDiscount : IDiscountStrategy { public decimal Calculate(Order o) => o.Total * 0.1m; }
public class SeasonalDiscount : IDiscountStrategy { public decimal Calculate(Order o) => DateTime.Now.Month == 12 ? 50 : 0; }
// 使用
var service = new OrderService(new VipDiscount());
// 或動態切換
service = new OrderService(new SeasonalDiscount());
// 參數化行為
public void ProcessData(IEnumerable data, Func filter, Action handler)
{
foreach (var item in data)
if (filter(item)) handler(item);
}
// 呼叫
ProcessData(numbers, n => n % 2 == 0, n => Console.WriteLine(n * 2));
public class Base
{
public Base() { Initialize(); } // 呼叫虛擬方法
protected virtual void Initialize() { }
}
public class Derived : Base
{
private readonly List _items = new(); // 3. 衍生欄位初始化
protected override void Initialize()
{
_items.Add("test"); // 5. 基底建構函式中呼叫,此時 _items 尚未初始化!
}
}
// 結果:NullReferenceException 在 _items.Add
public class Base
{
public Base() { } // 不呼叫虛擬方法
// 提供非虛擬初始化方法供衍生類別在建構函式末尾呼叫
protected void InitializeCore() { /* 共用邏輯 */ }
}
public class Derived : Base
{
private readonly List _items = new();
public Derived()
{
// 衍生建構函式本體執行時,所有欄位已初始化完成
_items.Add("safe");
InitializeCore();
}
}
| 技巧 | 核心關鍵字 | 違反後果 | 現代 C# 支援 |
|---|---|---|---|
| 介面導向 | interface, DI, ISP | 緊耦合、難測試、難替換 | record, primary constructor, required |
| 屬性封裝 | get/set/init, INotifyPropertyChanged | 脆弱、無驗證、序列化失控 | init, required, property patterns |
| 委派解耦 | event, Func/Action, Strategy | 硬編碼邏輯、單一職責違反 | pattern matching, delegate inference |
| 初始化順序 | field init, ctor chain, static ctor | NullReference、資源洩漏、行為異常 | primary constructor, init accessor |
這四個技巧貫穿 C# 程式設計的始終。熟練運用能寫出結構清晰、易於測試、擴充彈性、執行效能佳的專業級代碼。