字串一旦建立無法修改,任何「修改」操作都會建立新字串。頻繁拼接導致大量暫時物件、GC 壓力。解決:`StringBuilder`、字串插值 (編譯器優化)、`Span` 操作。
提供陣列/記憶體區段的**安全、無複製視圖**。`Span` 限 Stack-only (ref struct),高效能切片、解析、互操作;`Memory` 可放 Heap、跨 `await` 界限。
DLR (Dynamic Language Runtime) + CallSite 快取。首次呼叫建立表達式樹編譯委派 (慢),後續命中快取 (快)。無編譯期檢查、無 IntelliSense、無擴充方法支援。
| 型別 | 用途 | 特徵 |
| delegate | 自訂委派型別 | 可命名參數、支援 ref/out、可序列化 |
| Func<T, TResult> | 有回傳值委派 | 最多 16 參數、框架標準 |
| Action<T> | 無回傳值委派 | 最多 16 參數 |
| Expression<TDelegate> | 運算式樹 | 編譯時建立 AST、可分析/轉譯 (EF Core、LINQ to SQL) |
Q15: 事件標準模式?`event` 關鍵字限制?
public class Publisher
{
// 標準事件模式
public event EventHandler MyEvent;
protected virtual void OnMyEvent(MyEventArgs e)
{
MyEvent?.Invoke(this, e); // 執行緒安全調用 (C# 6 ?.Invoke)
}
}
public class MyEventArgs : EventArgs
{
public string Message { get; set; }
}
// event 限制:外部只能 += / -=,不能直接 = 指派、不能外部 Invoke
Q16: Lambda 閉包捕捉變數陷阱?
// ❌ 錯誤:捕捉迴圈變數 i (參考),所有委派共享同一個 i
var actions = new List();
for (int i = 0; i < 3; i++)
actions.Add(() => Console.WriteLine(i)); // 全部輸出 3
// ✅ 正確:捕捉區域複本
for (int i = 0; i < 3; i++)
{
int captured = i; // 每次迴圈新建區域變數
actions.Add(() => Console.WriteLine(captured)); // 輸出 0,1,2
}
Q17: `IQueryable` vs `IEnumerable` LINQ 執行差異?
- `IEnumerable`:記憶體中執行 (LINQ to Objects),立即/延遲執行看運算子
- `IQueryable`:運算式樹轉譯為 SQL (EF Core),**延遲執行**,`ToList()`/`First()` 觸發查詢
五、非同步程式設計
Q18: `async`/`await` 編譯器生成什麼?狀態機結構?
編譯器生成實作 `IAsyncStateMachine` 的結構體,包含狀態碼、建構子參數、區域變數欄位、`MoveNext()` 方法推進狀態、`SetStateMachine()` 設定狀態機。`await` 非阻塞釋放執行緒,完成時排程續體。
Q19: `Task` vs `ValueTask`?何時用 `ValueTask`?
- `Task`:參考型別、Heap 配置、通用非同步
- `ValueTask`:結構體、可避免配置 (同步完成/快取)、高頻呼叫、介面回傳
- 注意:`ValueTask` 只能 `await` 一次、不可多次 `await`、不可 `.Result`/`.Wait()`
Q20: `ConfigureAwait(false)` 作用?ASP.NET Core 需要嗎?
- 避免捕捉 `SynchronizationContext`,續體在 ThreadPool 執行,防止死結、提升效能
- ASP.NET Core **無 SynchronizationContext**,不需要 (但庫開發習慣加上)
- UI 應用 (WPF/WinForms/MAUI) **必須** 在非 UI 執行緒工作時使用
Q21: `CancellationToken` 傳遞與檢查模式?
public async Task ProcessAsync(CancellationToken ct = default)
{
foreach (var item in items)
{
ct.ThrowIfCancellationRequested(); // 拋 OperationCanceledException
await ProcessItemAsync(item, ct); // 傳遞給下層
}
}
// 使用
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await ProcessAsync(cts.Token);
Q22: 非同步避坑指南?
- ❌ `async void` (除非事件處理器) → 無法捕捉例外、無法等待
- ❌ `.Result` / `.Wait()` → 死結風險 (捕捉 SynchronizationContext)
- ❌ `Task.Run` 包裝同步 CPU 密集工作 → 偽非同步,佔用 ThreadPool
- ❌ `foreach` 中 `await` 可平行化時未用 `Parallel.ForEachAsync` / `Task.WhenAll`
- ✅ 參數預設 `CancellationToken ct = default`
- ✅ `using` / `await using` 管理資源
- ✅ `try/catch` 包裝 `await` (例外不會自動冒泡到呼叫端同步碼)
六、設計模式與架構
Q23: 單例模式執行緒安全實作?
// 最佳:Lazy (內建雙重檢查鎖定、延遲初始化)
public sealed class Singleton
{
private static readonly Lazy _instance = new(() => new Singleton());
public static Singleton Instance => _instance.Value;
private Singleton() { }
}
Q24: 工廠方法 vs 抽象工廠?
- 工廠方法:單一產品、單一方法、繼承點決定建立邏輯
- 抽象工廠:產品家族、多個建立方法、介面定義家族契約
Q25: 依賴注入三大生命週期?
| 生命週期 | 行為 | 適用場景 |
| Transient | 每次注入建立新實例 | 輕量、無狀態服務 |
| Scoped | 每個 Scope (HTTP 請求) 一個實例 | DbContext、工作單元、使用者語境 |
| Singleton | 應用程式存活期單一實例 | 快取、設定、無狀態單例服務 |
七、.NET 執行環境與工具
Q26: .NET Framework vs .NET Core vs .NET 5/6/7/8 差異?
- .NET Framework:Windows only、系統級安裝、4.8 為最終版
- .NET Core:跨平台、Side-by-side、開源、高效能
- .NET 5+:統一平台、單一 SDK、LTS 版本 (LTS: 6, 8)、原生 AOT、效能持續優化
Q27: JIT 編譯類型?Tiered Compilation?
- Standard JIT:方法首次呼叫編譯
- Tiered Compilation (預設開啟):Tier 0 快速編譯 (無優化) → 熱路徑升級 Tier 1 高度優化
- ReadyToRun (R2R) / AOT:發布時預編譯、啟動更快、體積較大
Q28: `dotnet` CLI 常用指令?
dotnet new console -n MyApp // 建立專案
dotnet build -c Release // 建置
dotnet run --project MyApp // 執行
dotnet test --logger trx // 測試
dotnet publish -c Release -o ./out // 發布
dotnet add package Newtonsoft.Json // 加入套件
dotnet tool install -g dotnet-ef // 安裝全域工具
Q29: 封裝部署模式?
- Framework-dependent:需目標機安裝 .NET Runtime、體積小
- Self-contained:內含 Runtime、無需安裝、體積大 (~60MB+)、支援單一檔案 `PublishSingleFile`
- Native AOT (.NET 7+):編譯為原生碼、極快啟動、無 JIT、體積小、受限制 (無反射/動態載入)
Q30: 如何診斷生產環境高 CPU / 記憶體洩漏?
- `dotnet-counters` 監控 CPU、GC、ThreadPool、自訂計數器
- `dotnet-dump` 收集核心傾印 → `dotnet-dump analyze` 檢查堆積、執行緒、同步區塊
- `dotnet-trace` 收集效能追蹤 (PerfView 分析)
- `dotnet-gcdump` 專門 GC 堆積快照
- Visual Studio 診斷工具 / PerfView / dotMemory / dotTrace
八、實戰編碼題
C1: 實作線程安全的單例字典快取
public class ThreadSafeCache
{
private readonly Dictionary _dict = new();
private readonly ReaderWriterLockSlim _lock = new();
public TValue GetOrAdd(TKey key, Func factory)
{
_lock.EnterUpgradeableReadLock();
try
{
if (_dict.TryGetValue(key, out var value)) return value;
_lock.EnterWriteLock();
try
{
// Double-check
if (_dict.TryGetValue(key, out value)) return value;
value = factory(key);
_dict[key] = value;
return value;
}
finally { _lock.ExitWriteLock(); }
}
finally { _lock.ExitUpgradeableReadLock(); }
}
}
C2: 實作非同步生產者消費者模式 (Channel)
var channel = Channel.CreateBounded(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait // 滿時阻塞
});
// 生產者
await channel.Writer.WriteAsync(item);
// 消費者
await foreach (var item in channel.Reader.ReadAllAsync())
{
await Process(item);
}
C3: 使用 Span 實作高效能字串分割 (無配置)
public static int CountWords(ReadOnlySpan text)
{
int count = 0;
bool inWord = false;
foreach (char c in text)
{
if (char.IsWhiteSpace(c))
{
inWord = false;
}
else if (!inWord)
{
inWord = true;
count++;
}
}
return count;
}
總結
這 30+ 核心問題涵蓋了 C# 面試的 80% 考點。準備策略:
- 理解原理:不死記答案,掌握底層機制 (GC、JIT、狀態機、DLR)
- 實作練習:手寫單例、工廠、非同步流、Span 操作
- 場景連結:每個知識點對應實際專案經驗 (效能優化、Bug 修復、架構決策)
- 承認未知:遇到不會的誠實說明,描述排查思路 (查文件、Benchmark、Profiling)
技術面試核心在於**解決問題的思維**,而非背誦 API。祝面試順利!
- 上一篇文章: C# DirectX 視訊播放實作指南:Microsoft.DirectX.AudioVideoPlayback 完整教學
- 下一篇文章: C# 動態載入/卸載非託管 DLL:以 LoadLibrary/GetProcAddress/FreeLibrary 實現熱重載
Copyright ©
程式碼之家 All Rights Reserved