後端語言
EP.02

C# 與 ASP.NET
.NET 工程師面試題

型別系統、LINQ、Web API 設計 — C# 語法特性與後端開發核心概念

Joseph Chen

2026
15 min read
TMS 專案實戰

在鴻海的 TMS 專案裡用過 ASP.NET,第一次看到 LINQ 的時候以為在看 SQL,後來才發現它是 C# 的內建語言整合查詢——比 Python 的 list comprehension 還強大。

C# 特色

C# 是 Microsoft 開發的強型別物件導向語言,2002 年隨 .NET Framework 發布。現在的 .NET Core / .NET 5+ 已跨平台支援 Windows/Linux/macOS,在企業後端開發是主流選項之一。

特性C#Java(對照)Python(對照)
型別系統強型別 + 型別推斷 (var)強型別動態型別
記憶體管理垃圾回收(GC)垃圾回收(GC)垃圾回收(GC)
async/await一等公民,語言原生支援需要第三方框架原生支援(asyncio)
LINQ內建語言整合查詢需要 Stream API無直接對應
跨平台.NET Core / .NET 5+原生跨平台(JVM)原生跨平台
主要用途Web API、企業系統、遊戲(Unity)Web、Android、企業腳本、AI、Web

C# 語法特色速查

C# 語法現代、簡潔,有幾個特色是面試常考、日常也常用的,熟悉它們能讓程式碼更安全、更易讀:

csharp_syntax.cs
// ── var 型別推斷(靜態,不是動態!)────────────────────────
var name = "Joseph";      // 編譯器推斷為 string,之後不能改型別
var count = 42;           // 編譯器推斷為 int
// name = 123;            // !! 編譯錯誤,var 是靜態推斷,不是動態型別

// ── Null 安全操作符 ───────────────────────────────────────
string? maybeNull = null;           // ? 代表 nullable string

// ?. (null conditional):maybeNull 若為 null,整個表達式返回 null
int? len = maybeNull?.Length;       // len = null(不會 NullReferenceException)

// ?? (null coalescing):左邊為 null 時使用右邊的值
string display = maybeNull ?? "N/A";  // display = "N/A"

// ??= (null coalescing assignment):左邊為 null 才賦值
maybeNull ??= "default";              // 等同 if(maybeNull == null) maybeNull = "default"

// ── String Interpolation ──────────────────────────────────
string firstName = "Joseph";
int age = 28;
string msg = $"Hello, {firstName}! You are {age} years old.";
// 等同舊寫法:string.Format("Hello, {0}! You are {1} years old.", firstName, age)

// ── Properties(屬性)─────────────────────────────────────
public class Person {
    // 自動屬性:編譯器自動產生 backing field
    public string Name { get; set; }           // 可讀可寫
    public int Age { get; private set; }       // 只能在類別內部修改
    public string Id { get; init; }            // 只能在初始化時設定(C# 9+)

    // 計算屬性:不儲存值,每次讀取時計算
    public bool IsAdult => Age >= 18;          // 等同 get { return Age >= 18; }
}

LINQ(Language Integrated Query)

LINQ 是 C# 最強大的特性之一,讓你用類似 SQL 的語法操作任何集合(List、陣列、資料庫、XML)。一旦用習慣,寫不帶 LINQ 的 C# 會很難受。

Deferred Execution(延遲執行)

LINQ 查詢是 lazy(懶) 的:建立查詢時不執行,只有在實際迭代(foreach、.ToList()、.Count()...)時才執行。這意味著可以先建立查詢描述,之後再決定是否執行,但也要注意多次迭代會多次執行查詢。

linq_demo.cs
using System;
using System.Collections.Generic;
using System.Linq;

var students = new List<Student> {
    new("Alice",  3.8, "CS"),
    new("Bob",    2.9, "EE"),
    new("Charlie",3.5, "CS"),
    new("Diana",  3.9, "Math"),
    new("Eve",    3.1, "CS"),
};

// ── Method Syntax(最常用)──────────────────────────────────
// Where:過濾
var csStudents = students.Where(s => s.Major == "CS");

// Select:投影(類似 map)
var names = students.Select(s => s.Name);

// OrderBy / OrderByDescending:排序
var byGpa = students.OrderByDescending(s => s.GPA);

// FirstOrDefault:取第一個符合條件的,找不到返回 null(不拋例外)
var topCS = students.FirstOrDefault(s => s.Major == "CS" && s.GPA > 3.7);

// GroupBy:分組
var byMajor = students.GroupBy(s => s.Major);
foreach (var group in byMajor) {
    Console.WriteLine($"{group.Key}: {group.Count()} 人, 平均 GPA: {group.Average(s => s.GPA):F2}");
}

// 鏈式操作(最常見的實際用法)
var result = students
    .Where(s => s.GPA >= 3.5)
    .OrderByDescending(s => s.GPA)
    .Select(s => new { s.Name, s.GPA })
    .ToList();   // ← ToList() 才真正執行查詢(觸發 deferred execution)

// ── Query Syntax(較少用,更像 SQL)──────────────────────────
var queryResult =
    from s in students
    where s.GPA >= 3.5
    orderby s.GPA descending
    select new { s.Name, s.GPA };

record Student(string Name, double GPA, string Major);

async / await

C# 的 async/await 是非同步程式設計的核心,讓非同步程式碼讀起來像同步程式碼。在 ASP.NET Web API 裡幾乎所有 I/O 操作都應該非同步。

Task vs Task<T>

Task 代表「沒有返回值的非同步操作」(等同 void);Task<T> 代表「返回 T 型別結果的非同步操作」。

避免 async void

async void 無法被 await,例外發生時無法被捕獲,會直接崩潰整個程式。唯一例外是事件處理器(event handler)。其他情況一律用 async Task。

Deadlock 陷阱

在 ASP.NET 環境中,不要對 async 方法呼叫 .Result 或 .Wait()(同步阻塞),這會造成 deadlock。永遠用 await 等待。

async_controller.cs
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase {
    private readonly IUserService _userService;

    // 依賴注入(DI):不自己 new,讓框架注入
    public UsersController(IUserService userService) {
        _userService = userService;
    }

    // async Task<IActionResult>:非同步方法,有返回值
    [HttpGet("{id}")]
    public async Task<IActionResult> GetUser(int id) {
        // await:非同步等待,不阻塞執行緒
        var user = await _userService.GetByIdAsync(id);
        if (user == null) return NotFound();
        return Ok(user);
    }

    [HttpGet]
    public async Task<IActionResult> GetAll([FromQuery] string? search) {
        var users = await _userService.GetAllAsync(search);
        return Ok(users);
    }
}

// Service 層的 async 方法
public class UserService : IUserService {
    private readonly AppDbContext _db;
    public UserService(AppDbContext db) { _db = db; }

    public async Task<User?> GetByIdAsync(int id) {
        // Entity Framework 的非同步查詢
        return await _db.Users.FindAsync(id);
    }

    public async Task<List<User>> GetAllAsync(string? search) {
        return await _db.Users
            .Where(u => search == null || u.Name.Contains(search))
            .OrderBy(u => u.Name)
            .ToListAsync();  // EF Core 的非同步版 ToList()
    }
}

ASP.NET Web API 基礎

ASP.NET Core 的 Web API 採用 MVC 架構,Controller 負責處理 HTTP 請求。以下是一個完整的 CRUD Controller 骨架:

ProductsController.cs
[ApiController]
[Route("api/[controller]")]                        // 路由:api/products
public class ProductsController : ControllerBase {
    private readonly IProductService _service;
    public ProductsController(IProductService service) => _service = service;

    // GET api/products
    // GET api/products?category=electronics
    [HttpGet]
    public async Task<ActionResult<IEnumerable<Product>>> GetAll(
        [FromQuery] string? category) {            // [FromQuery]:從 URL query string 取值
        var items = await _service.GetAllAsync(category);
        return Ok(items);
    }

    // GET api/products/5
    [HttpGet("{id:int}")]
    public async Task<ActionResult<Product>> GetById(
        [FromRoute] int id) {                      // [FromRoute]:從 URL 路由取值
        var item = await _service.GetByIdAsync(id);
        return item is null ? NotFound() : Ok(item);
    }

    // POST api/products
    [HttpPost]
    public async Task<ActionResult<Product>> Create(
        [FromBody] CreateProductDto dto) {         // [FromBody]:從 HTTP body (JSON) 反序列化
        if (!ModelState.IsValid) return BadRequest(ModelState);
        var created = await _service.CreateAsync(dto);
        return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
    }

    // PUT api/products/5
    [HttpPut("{id:int}")]
    public async Task<IActionResult> Update(int id, [FromBody] UpdateProductDto dto) {
        var success = await _service.UpdateAsync(id, dto);
        return success ? NoContent() : NotFound();
    }

    // DELETE api/products/5
    [HttpDelete("{id:int}")]
    public async Task<IActionResult> Delete(int id) {
        var success = await _service.DeleteAsync(id);
        return success ? NoContent() : NotFound();
    }
}

// Data Transfer Objects(DTO):只暴露 API 需要的欄位,不直接暴露 Entity
public record CreateProductDto(string Name, decimal Price, string Category);
public record UpdateProductDto(string? Name, decimal? Price);

Middleware Pipeline

ASP.NET Core 的請求依序通過 Middleware Pipeline:Authentication → Authorization → Routing → Controller。每個 Middleware 可以決定要不要繼續傳遞請求。在 Program.cs 裡用 app.UseXxx() 的順序很重要,先加的先執行。

Dependency Injection(DI)

DI 是 ASP.NET Core 的核心設計:類別不自己 new 依賴,而是透過建構函數宣告需要什麼,由框架負責「注入」。好處是低耦合、易測試。

生命週期說明適合場景注冊方式
Singleton整個應用程式生命週期只有一個實例,所有請求共用快取、設定、loggingAddSingleton<T>()
Scoped每個 HTTP 請求建立一個新實例,同一請求內共用DbContext、業務服務AddScoped<T>()
Transient每次注入都建立新實例輕量、無狀態的工具類AddTransient<T>()
Program.cs
var builder = WebApplication.CreateBuilder(args);

// 在這裡向 DI Container 注冊服務
builder.Services.AddControllers();

// Scoped:每個 HTTP 請求一個 DbContext(標準做法)
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

// Scoped:業務服務(同一請求內共用同一個 UserService 實例)
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IProductService, ProductService>();

// Singleton:快取服務(整個 App 共用)
builder.Services.AddSingleton<ICacheService, MemoryCacheService>();

var app = builder.Build();

// Middleware Pipeline(順序重要!)
app.UseHttpsRedirection();
app.UseAuthentication();      // 先驗證身份
app.UseAuthorization();       // 再檢查權限
app.MapControllers();

app.Run();

面試常考題

Q1. C# 的 struct 和 class 差異?

struct 是 Value Type,存在 Stack 上,賦值時複製整個值(互相獨立)。class 是 Reference Type,存在 Heap 上,賦值時複製的是參考(指向同一個物件)。struct 適合小型不可變資料(Point、Color);class 適合複雜物件。

Q2. LINQ 的 deferred execution 是什麼意思?

LINQ 查詢在建立時不立即執行,只是描述「要怎麼查」。真正執行發生在迭代時(foreach、ToList()、Count()、First()...)。好處是可以組合多個查詢再一次執行;壞處是若資料源在查詢建立後被修改,結果會反映最新狀態。

Q3. async/await 和 Thread 的差異?

Thread 是 OS 層級的執行緒,佔用真實資源(~1MB stack)。async/await 是非同步模型,等待 I/O 時釋放執行緒去做其他事,I/O 完成後繼續。結果是:1000 個並發 HTTP 請求,Thread 模型需要 1000 個執行緒;async 模型可能只需幾十個執行緒,大幅提升吞吐量。

Q4. interface 和 abstract class 的使用時機?

interface 定義「能做什麼」(行為契約),一個 class 可以實作多個 interface。abstract class 定義「是什麼」(繼承關係),可以包含部分實作。DI 幾乎都用 interface(IUserService)。若類別之間有 is-a 關係且需共用程式碼,用 abstract class。

Q5. DI 中 Scoped 和 Singleton 的差異?

Singleton:整個應用程式只有一個實例,所有請求共用,必須是執行緒安全的。Scoped:每個 HTTP 請求有獨立實例,同一請求內共用(例如同一請求的 Controller 和 Service 共用同一個 DbContext),不需要執行緒安全考量。

Q6. .NET 5/6/7/8 和 .NET Framework 的差異?

.NET Framework(1.0–4.8)只能跑在 Windows,已進入維護模式。.NET Core / .NET 5+(統一品牌)是跨平台的現代版本,效能更好、更輕量。新專案全部用 .NET 8(目前 LTS),不要用舊的 .NET Framework,除非維護遺留系統。

C#
ASP.NET
LINQ
Web API
.NET
面試題
EP.02