首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 面試必考題庫:基礎語法、記憶體管理、設計模式、非同步程式設計完整解析

     編輯:關於C#

前言

這份題庫整理了 C# 開發者面試中**最高頻率**的核心問題,涵蓋語法基礎、記憶體模型、型別系統、泛型、委派/事件、LINQ、非同步、設計模式、.NET 執行環境等關鍵領域。每題附帶標準答案、深度解析與常見陷阱,適合面試準備與知識查漏補缺。

一、語法基礎與型別系統

Q1: `struct` vs `class` 差異?何時使用 struct?

標準答案:

使用 struct 原則: 小型 (<16 bytes)、不可變、邏輯上為單一值、高頻建立/銷毀 (避免 GC 壓力)。例:`Point`、`DateTime`、`Guid`、自訂座標、錢幣金額。

Q2: `ref`、`out`、`in`、`params` 差異?


// ref: 雙向傳遞,呼叫端需初始化
void Swap(ref int a, ref int b) { int t=a; a=b; b=t; }

// out: 單向輸出,呼叫端無需初始化,方法內必須指派
bool TryParse(string s, out int result) { result=0; return int.TryParse(s,out result); }

// in: 唯讀參考傳遞 (C# 7.2),避免大型 struct 複製,防止修改
void ReadOnly(in BigStruct data) { /* data 不可修改 */ }

// params: 參數陣列,只能有一個且必須是最後一個
int Sum(params int[] numbers) { return numbers.Sum(); }

Q3: `const` vs `readonly` vs `static readonly`?

特性class (參考型別)struct (實值型別)
記憶體配置Managed HeapStack (區域變數) / Inline (陣列/欄位)
指派行為複製參考複製值 (完整複製)
預設值null所有欄位為 0/false/null
繼承支援繼承、多型不可繼承 (隱性 sealed),可實作介面
解構子可有 Finalizer不可有 Finalizer
null 能力可為 null預設不可為 null (需 `Nullable`)

Q4: 介面 vs 抽象類別?

介面:定義契約、多重實作、無狀態、無建構子、預設方法 (C# 8+)。
抽象類別:共享實作、狀態、建構子、單一繼承、存取修飾詞控制。
選擇:是「是一種」關係且需共享程式碼 → 抽象類別;是「能做什麼」契約 → 介面。

Q5: `string` 不可變性的含義與效能影響?

字串一旦建立無法修改,任何「修改」操作都會建立新字串。頻繁拼接導致大量暫時物件、GC 壓力。解決:`StringBuilder`、字串插值 (編譯器優化)、`Span` 操作。

二、記憶體管理與 GC

Q6: Stack vs Heap 儲存什麼?實值型別一定在 Stack 嗎?

  • Stack:區域變數參考、實值型別區域變數、方法呼叫框架
  • Heap:參考型別物件實例、實值型別欄位 (在物件內)、靜態欄位、陣列元素
  • 實值型別不一定在 Stack:作為類別欄位、陣列元素、Boxing 後在 Heap

Q7: GC 代系機制?Gen 0/1/2 差異?

  • Gen 0:新建立、短壽命物件 (最常回收、最快)
  • Gen 1:倖存 Gen 0 的物件、緩衝區
  • Gen 2:長壽命物件、大型物件 (>85KB 進 LOH)、靜態資料
  • 大型物件堆積 (LOH) 不壓縮、只 Gen 2 回收、易碎片化

Q8: `IDisposable` 模式標準實作?`using` / `await using` 原理?


public class ResourceHolder : IDisposable
{
    private bool _disposed;
    private SafeHandle _handle; // 非受控資源包裝

    public void Dispose() => Dispose(true);

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;
        if (disposing) _handle?.Dispose(); // 釋放受控資源
        _handle = null; // 釋放非受控資源
        _disposed = true;
    }

    ~ResourceHolder() => Dispose(false); // Finalizer 保底
}

// using 編譯為 try/finally 呼叫 Dispose()
// await using (C# 8) 編譯為 try/finally 呼叫 DisposeAsync() (IAsyncDisposable)

Q9: `WeakReference` 用途?

建立不阻止 GC 回收的參考。適用於快取、觀察者模式避免記憶體洩漏、大型物件選擇性保留。

Q10: `Span` / `Memory` 如何避免配置?

提供陣列/記憶體區段的**安全、無複製視圖**。`Span` 限 Stack-only (ref struct),高效能切片、解析、互操作;`Memory` 可放 Heap、跨 `await` 界限。

三、泛型與進階型別

Q11: 泛型約束 `where T : new()` / `class` / `struct` / `notnull` / `unmanaged` / `delegate` / `enum`?


void M1() where T : new() { }           // 公用無參建構子
void M2() where T : class { }           // 參考型別
void M3() where T : struct { }          // 實值型別 (非 Nullable)
void M4() where T : notnull { }         // 非 null 型別 (C# 8)
void M5() where T : unmanaged { }       // 無參考欄位的實值型別 (C# 7.3)
void M6() where T : Delegate { }        // 委派型別 (C# 7.3)
void M7() where T : Enum { }            // 列舉型別 (C# 7.3)
void M8() where T : BaseClass, IInterface { } // 多重約束

Q12: 共變性 / 逆變性 / 不變性?`in` / `out` 關鍵字?

  • 共變性 (`out`):`IEnumerable` - 衍生型別可指派給基底型別。`IEnumerable` → `IEnumerable` ✅
  • 逆變性 (`in`):`Action` - 基底型別可指派給衍生型別。`Action` → `Action` ✅
  • 不變性:`List`、陣列 `T[]` (陣列有執行期檢查的共變性,但不安全)
  • Q13: `dynamic` 底層原理?效能影響?

    DLR (Dynamic Language Runtime) + CallSite 快取。首次呼叫建立表達式樹編譯委派 (慢),後續命中快取 (快)。無編譯期檢查、無 IntelliSense、無擴充方法支援。

    四、委派、事件、Lambda、LINQ

    Q14: `delegate`、`Func`、`Action`、`Expression` 差異?

特性constreadonlystatic readonly
初始化時機編譯期 (必須為常值)執行期 (建構子)執行期 (靜態建構子/欄位初始化)
記憶體不佔用實例記憶體 (編譯期內嵌)每實例一份全域一份
可用型別基本型別、string、enum、null任意型別任意型別
版本問題跨組件需重新編譯消費端無版本問題無版本問題

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: 依賴注入三大生命週期?

型別用途特徵
delegate自訂委派型別可命名參數、支援 ref/out、可序列化
Func<T, TResult>有回傳值委派最多 16 參數、框架標準
Action<T>無回傳值委派最多 16 參數
Expression<TDelegate>運算式樹編譯時建立 AST、可分析/轉譯 (EF Core、LINQ to SQL)

七、.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% 考點。準備策略:

  1. 理解原理:不死記答案,掌握底層機制 (GC、JIT、狀態機、DLR)
  2. 實作練習:手寫單例、工廠、非同步流、Span 操作
  3. 場景連結:每個知識點對應實際專案經驗 (效能優化、Bug 修復、架構決策)
  4. 承認未知:遇到不會的誠實說明,描述排查思路 (查文件、Benchmark、Profiling)

技術面試核心在於**解決問題的思維**,而非背誦 API。祝面試順利!

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
生命週期行為適用場景
Transient每次注入建立新實例輕量、無狀態服務
Scoped每個 Scope (HTTP 請求) 一個實例DbContext、工作單元、使用者語境
Singleton應用程式存活期單一實例快取、設定、無狀態單例服務