Ngân hàng câu hỏi

Ngân hàng câu hỏi C#

60+ câu hỏi C# fresher/junior hay gặp nhất ở phỏng vấn .NET tại HCM — kèm đáp án ngắn gọn.

A. Kiểu dữ liệu & bộ nhớ

1. Value type vs Reference type khác nhau ở đâu?

  • Value type (int, bool, struct, enum): lưu trực tiếp giá trị, thường trên stack.
  • Reference type (class, string, array): lưu địa chỉ đến object trên heap.
  • Khi truyền vào method: value type copy giá trị, reference type copy địa chỉ.
  • Default: value type = 0/false; reference type = null.

2. Boxing và Unboxing là gì?

Boxing = convert value type → object (cấp phát heap).
Unboxing = convert ngược lại, cần cast.

int x = 5;
object o = x;      // boxing
int y = (int)o;    // unboxing

Tránh boxing bằng cách dùng Generic (List<int> thay ArrayList).

3. string là value type hay reference type?

Reference type nhưng immutable. So sánh == so sánh nội dung nhờ overload. Có string interning — string literal giống nhau dùng chung 1 object.

4. string immutable nghĩa là gì? Vì sao nên dùng StringBuilder?

Immutable = không sửa được sau khi tạo. Mọi thao tác như s + "x" tạo string MỚI. Vòng lặp lớn → tạo nhiều rác.
StringBuilder dùng mảng char[] mutable → append O(1) amortized, không cấp phát mới mỗi lần.

5. const vs readonly vs static readonly?

  • const: compile-time, primitive only, inline vào nơi dùng. Đổi cần build lại.
  • readonly: set trong ctor, mỗi instance khác nhau, mọi kiểu.
  • static readonly: 1 lần khi class load, share toàn app, mọi kiểu.

6. Nullable type — int? thực chất là gì?

int? = Nullable<int> — struct chứa Value + HasValue. Khi null, HasValue = false. Truy cập .Value khi null → InvalidOperationException.

7. var, dynamic, object khác nhau gì?

  • var: compile-time, compiler suy luận từ vế phải. Vẫn type-safe.
  • dynamic: runtime, bỏ qua type check, dễ runtime error.
  • object: tĩnh là base type, dùng phải cast.

8. So sánh ==, Equals, ReferenceEquals?

  • ==: với reference type mặc định so sánh tham chiếu, trừ khi overload (như string).
  • Equals: instance method, override được — so sánh logic.
  • ReferenceEquals: luôn so sánh tham chiếu, bỏ qua override.

9. this có dùng được trong static method không?

Không. Static method thuộc class, không có instance để this trỏ tới. Lỗi compile.

10. Khi nào dùng struct, khi nào dùng class?

  • struct: dữ liệu nhỏ (≤ 16 bytes), không có identity, immutable thì càng tốt (Point, DateTime).
  • class: object có behavior, share tham chiếu, kích thước lớn, cần kế thừa.

B. Exception & null

11. throw vs throw ex — khác gì?

  • throw; → giữ nguyên stack trace gốc.
  • throw ex; → reset stack trace, mất thông tin nơi lỗi ban đầu. Tránh.

12. try / finally không có catch có hợp lệ không?

Có. Dùng khi muốn cleanup (đóng file, dispose) nhưng vẫn để exception lan lên.

13. using block thực chất là gì?

Cú pháp tắt cho try / finally { obj.Dispose(); }. Object phải implement IDisposable. C# 8+ có using var x = ... không cần {}.

14. ????= làm gì?

  • a ?? b: nếu a null trả b, ngược lại trả a.
  • a ??= b: gán a = b nếu a đang null.

15. ?. (null-conditional) là gì?

a?.B?.C — nếu bất kỳ phần nào null, trả về null, không throw NullReferenceException.

C. Collections & Generic

16. List<T> vs Array — khác gì?

  • Array: size cố định, performance tốt nhất.
  • List<T>: size động, dùng array bên trong, double size khi đầy.

17. Generic giải quyết vấn đề gì?

  • Type safety compile-time (không cast).
  • Tránh boxing với value type.
  • Tái sử dụng code cho nhiều kiểu.

18. Dictionary<K,V> vs HashSet<T>?

  • Dictionary: lưu cặp key → value, key unique.
  • HashSet: chỉ lưu element unique, dùng cho kiểm tra tồn tại nhanh.

Cả hai dùng hash, lookup O(1) avg.

19. Khi nào List<T> resize?

Khi Count == Capacity, tạo array mới gấp đôi rồi copy. Vì sao Add amortized O(1) — chi phí resize được trải đều.

20. IEnumerable vs ICollection vs IList?

  • IEnumerable<T>: chỉ duyệt (foreach).
  • ICollection<T>: thêm Add/Remove/Count.
  • IList<T>: thêm indexer [i]Insert/RemoveAt.

Khi định nghĩa API public, expose interface nhỏ nhất đủ dùng.

21. yield return làm gì?

Tạo iterator lười — sinh từng phần tử khi được duyệt, không build toàn bộ collection.

public IEnumerable<int> Take5() {
    for (int i = 0; i < 5; i++) yield return i;
}

D. OOP trong C# / Method

22. Overload vs Override?

  • Overload: cùng tên, khác signature, trong cùng class — compile-time.
  • Override: redefine method virtual ở class con — runtime.

23. virtual, abstract, override, new?

  • virtual: cho phép class con override.
  • abstract: chỉ khai báo, class con bắt buộc implement.
  • override: class con redefine method virtual/abstract.
  • new: ẩn method của class cha (không polymorphism).

24. sealed là gì, dùng khi nào?

  • sealed class: không cho kế thừa.
  • sealed override: class con không cho override tiếp.

Dùng cho Singleton, immutable class, hoặc lý do bảo mật/performance.

25. Method chaining — viết thế nào?

Method trả về this để gọi tiếp:

public class Query {
    public Query Where(string c) { ... return this; }
    public Query OrderBy(string c) { ... return this; }
}

E. Delegate, Event, Lambda

26. Delegate là gì?

Type-safe function pointer — biến giữ tham chiếu tới method (instance hoặc static) có signature khớp.

27. Action, Func, Predicate — khác gì?

  • Action<T>: không trả về.
  • Func<T1, ..., TResult>: trả về TResult (cuối).
  • Predicate<T> = Func<T, bool>.

28. Event vs Delegate public — vì sao event tốt hơn?

Class ngoài chỉ += / -= được — không thể invoke, không thể gán null xoá subscriber khác.

29. Closure trong C# — ví dụ?

Lambda "bắt" biến từ scope ngoài:

int x = 10;
Func<int, int> add = n => n + x;
x = 20;
add(5);   // = 25 (đọc x mới nhất!)

30. Khi nào dùng static class?

Khi không cần instance — chỉ chứa utility method/extension. Ví dụ: Math, Console, lớp chứa extension method.

F. LINQ

31. LINQ là gì?

Language Integrated Query — cú pháp query trên collection (IEnumerable/IQueryable). Thực chất là chuỗi extension method.

32. Where vs Select?

  • Where: filter — trả phần tử thoả điều kiện.
  • Select: project — biến đổi từng phần tử.

33. First vs FirstOrDefault vs Single?

  • First: trả phần tử đầu, không có → exception.
  • FirstOrDefault: trả phần tử đầu, không có → default (null/0).
  • Single: yêu cầu đúng 1 phần tử, nhiều hơn cũng exception.

34. IEnumerable vs IQueryable?

  • IEnumerable: query chạy in-memory.
  • IQueryable: build expression tree → dịch sang SQL → chạy ở DB.

Trong EF Core, filter trên IQueryable mới được đẩy xuống SQL.

35. Deferred execution là gì?

LINQ query không chạy ngay — chỉ chạy khi iterate (foreach) hoặc gọi ToList(), Count(), First()...

G. Async / Multithreading

36. async/await có tạo thread mới không?

Không. Khi gặp await, method giải phóng thread hiện tại, return Task. Khi I/O xong, continuation được schedule tiếp (có thể trên thread khác).

37. Task vs Thread khác nhau?

  • Thread: OS thread, heavy.
  • Task: abstraction trên ThreadPool, có giá trị trả về, hỗ trợ continuation, await, cancellation.

38. Khi nào dùng Task.Run?

Chỉ cho CPU-bound — offload công việc nặng sang thread pool. I/O-bound dùng async/await trực tiếp.

39. Vì sao tránh .Result / .Wait() trên Task?

Block thread → trong môi trường có SynchronizationContext (ASP.NET classic, WPF) → deadlock.

40. async void có nên dùng?

Không — trừ event handler. Không await được, exception bị nuốt.

41. lock trong C# bản chất là gì?

lock (obj) { ... }

Tương đương Monitor.Enter(obj); try {...} finally { Monitor.Exit(obj); }. Đảm bảo chỉ 1 thread vào critical section tại 1 thời điểm.

42. Interlocked dùng khi nào?

Thao tác atomic trên primitive (Increment, Add, Exchange). Nhanh hơn lock cho counter đơn giản.

43. ConfigureAwait(false) để làm gì?

Không cố quay về SynchronizationContext gốc sau await. Dùng trong library code để tránh deadlock và tăng performance.

H. Memory & GC

44. Garbage Collection trong .NET hoạt động ra sao?

GC theo generational: Gen 0 (object mới), Gen 1, Gen 2 (sống lâu). GC quét Gen 0 thường xuyên, Gen 2 ít. Object không còn reference nào → bị thu hồi.

45. IDisposable để làm gì?

Cho phép release unmanaged resource (file handle, DB connection, socket). GC không tự lo unmanaged → cần Dispose().

46. Finalize vs Dispose?

  • Dispose: gọi thủ công / qua using, giải phóng resource ngay.
  • Finalize (~destructor): GC gọi trước khi reclaim, không định thời điểm — chỉ dùng cho safety net cho unmanaged resource.

47. Memory leak có thể xảy ra trong C# không?

Có. Dù có GC, leak xảy ra khi reference vẫn còn — event handler chưa -=, static collection giữ object, scoped service bị singleton ôm.

I. C# 9–13 Features (modern)

48. record là gì?

Class/struct với value semantics: equality so sánh property, có with expression để clone-and-modify, ToString tự sinh.

public record User(string Name, int Age);
var u2 = u1 with { Age = 30 };

49. Pattern matching trong C#?

if (obj is User { Age: > 18 } u) { ... }
string label = shape switch {
    Circle c => "circle",
    Square s => "square",
    _ => "unknown"
};

50. Top-level statements?

.NET 6+: Program.cs không cần class/Main, viết code thẳng.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run();

51. File-scoped namespace?

namespace MyApp.Services;   // không cần { }
public class UserService {}

52. Init-only property?

public class User { public string Name { get; init; } }
var u = new User { Name = "A" };  // OK
u.Name = "B";   // ❌ compile error

J. Câu gài & nâng cao

53. out vs ref vs in?

  • out: phải gán bên trong method, không cần init.
  • ref: phải init trước, hai chiều.
  • in: read-only reference, tránh copy struct lớn.

54. Mutable struct có nguy hiểm gì?

Vì struct là value type, modify field qua property của copy không ảnh hưởng bản gốc. Khuyến cáo: struct nên immutable.

55. Override Equals thì cũng phải override GetHashCode — vì sao?

Hợp đồng: a.Equals(b) == truea.GetHashCode() == b.GetHashCode(). Nếu vi phạm → Dictionary/HashSet hoạt động sai.

56. params keyword?

Cho phép số đối số biến đổi:

public int Sum(params int[] nums) => nums.Sum();
Sum(1, 2, 3, 4);

57. Extension method viết thế nào?

public static class StringExt {
    public static bool IsEmail(this string s) => s.Contains('@');
}
"a@b.com".IsEmail();

58. Sự khác giữa IEquatable<T>.Equalsobject.Equals?

IEquatable<T>.Equals(T) strong-typed, không boxing. object.Equals(object) cần cast nội bộ. Implement IEquatable<T> cho hiệu năng tốt trong Generic collection.

59. volatile keyword?

Báo compiler không tối ưu cache giá trị field — luôn đọc/ghi từ memory. Dùng cho variable share giữa thread (nhưng thường nên dùng lock hoặc Interlocked).

60. unsafe block dùng khi nào?

Khi cần pointer (int*) — interop với native lib, performance critical code (image processing). Rất hiếm dùng trong dự án thường.

61. Span<T>Memory<T> để làm gì?

Đại diện đoạn liên tục bộ nhớ (slice của array/string/stack-alloc) — không cấp phát mới. Tăng performance khi parse, không cần Substring tốn kém.

62. C# có hỗ trợ đa kế thừa class không?

Không với class — chỉ kế thừa 1 base class. Nhưng class có thể implement nhiều interface.


➡️ Tiếp theo: Ngân hàng câu hỏi OOP & SOLID

© 2026 .NET Fresher Guide. All rights reserved.

Về Trang Web

Hướng dẫn toàn diện để chuẩn bị phỏng vấn vị trí .NET Fresher với nội dung từ lý thuyết đến thực hành.