CodeGym /Các khóa học /C# SELF /Làm quen với exception và hệ thống phân cấp của chúng

Làm quen với exception và hệ thống phân cấp của chúng

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Hãy tưởng tượng bạn đang viết một cái máy tính để chia hai số. Mọi thứ đều ổn cho đến khi ai đó quyết định chia cho số không:

int a = 10;
int b = 0;
int result = a / b; // Bùm!
Chia cho số không sẽ gây ra exception

Lúc này chương trình "sập", và người dùng sẽ thấy một thông báo lỗi đáng sợ. Đó chính là tình huống ngoại lệ — một sự kiện không nên xảy ra trong kịch bản "bình thường" của chương trình và cần được chú ý đặc biệt.

Exception — là một cơ chế đặc biệt mà .NET dùng để báo cho chương trình của bạn biết rằng có gì đó bất thường xảy ra, và việc thực thi cần phải dừng lại hoặc xử lý theo cách đặc biệt.

Khi có lỗi runtime (ví dụ chia cho số không, truy cập file không tồn tại, hoặc — người quen cũ — dereference null), .NET sẽ "ném" (throw) một object exception đặc biệt. Điều này dẫn đến việc tìm kiếm handler có thể "bắt" (catch) tình huống đó và biết cách phản ứng.

Tại sao không trả về "mã lỗi"?

Có thể trả về mã lỗi — và nhiều ngôn ngữ cổ (Pascal, C, thậm chí một số API hiện đại) làm như vậy. Nhưng cách này bất tiện và nguy hiểm: rất dễ quên kiểm tra mã lỗi (ai mà chẳng hy vọng mọi thứ đều ổn) — và cực kỳ khó để hiểu chính xác chỗ nào bị lỗi. Exception cho phép bạn theo dõi lỗi một cách tập trung và phản ứng linh hoạt, không làm code chính bị ngập trong đống kiểm tra.

2. "Ném" exception

Exception có thể tự động phát sinh — khi có lỗi runtime, hoặc được ném thủ công bằng từ khóa throw:


int[] arr = new int[2];
arr[10] = 5; // exception sẽ tự động được ném System.IndexOutOfRangeException

throw new Exception("Thảm họa tuyệt đối!"); // tự tay ném exception Exception

Dù bạn nghĩ chương trình của mình thiết kế hoàn hảo đến đâu, luôn có thể có chuyện bất ngờ: người dùng đóng file, mất mạng, ai đó rút điện máy tính (gần như thế).

May mà .NET không chỉ cho chúng ta cơ chế "ném" exception, mà còn có cả công cụ để bắt và xử lý chúng — chúng ta sẽ bàn về chúng ngay sau đây.

3. Vòng đời của exception: từ throw đến catch

Khi có chuyện không vui trong code, .NET sẽ kích hoạt "quy trình khẩn cấp":

  1. Tạo object exception (ví dụ, IndexOutOfRangeException).
  2. Ném nó bằng từ khóa throw.
  3. Tìm handler: duyệt stack call từ dưới lên (từ method hiện tại lên method gọi nó) — tìm block catch đầu tiên phù hợp với loại exception vừa ném.
  4. Nếu tìm thấy handler — chương trình tiếp tục thực thi trong block catch.
  5. Nếu không tìm thấy handler nào — chương trình sẽ crash và in ra "stack trace" với chi tiết lỗi.

Nhìn giống như quả bóng nảy trên cầu thang — cho đến khi gặp người bắt, nó có thể "nảy" khá lâu trên stack.

4. Hệ thống phân cấp exception trong .NET

Cây "cha con"

Trong .NET (giống hầu hết ngôn ngữ OOP), exception được hiện thực dưới dạng class, kế thừa lẫn nhau và tạo thành một hệ thống phân cấp.

Tất cả đều có chung một tổ tiên — class cơ sở System.Exception.


System.Object
    └─ System.Exception
         ├─ System.SystemException
         │     ├─ System.NullReferenceException
         │     ├─ System.IndexOutOfRangeException
         │     ├─ System.DivideByZeroException
         │     ├─ System.OutOfMemoryException
         │     └─ ... và nhiều cái khác
         ├─ System.IO.IOException
         │     ├─ System.IO.FileNotFoundException
         │     ├─ System.IO.DirectoryNotFoundException
         │     └─ ...
         ├─ System.ArgumentException
         │     ├─ System.ArgumentNullException
         │     └─ System.ArgumentOutOfRangeException
         └─ (các Exception-class tự định nghĩa)
Sơ đồ hệ thống phân cấp exception trong .NET

Tại sao lại là class?

Nhờ hệ thống class, bạn có thể bắt luôn cả một "loại" vấn đề — ví dụ, tất cả lỗi liên quan đến tham số hàm (ArgumentException và các class con của nó). Hơn nữa, bạn có thể tự định nghĩa loại lỗi riêng (phần này sẽ nói ở bài sau).

Những exception phổ biến nhất trong .NET

  • NullReferenceException — cố gắng truy cập method hoặc property của object bằng null. Người quen cũ!
  • DivideByZeroException — chia cho số không.
  • IndexOutOfRangeException — truy cập ngoài phạm vi mảng.
  • ArgumentException, ArgumentNullException, ArgumentOutOfRangeException — tham số method bị lỗi.
  • FileNotFoundException, IOException — lỗi khi làm việc với file.
  • InvalidOperationException — thao tác không hợp lệ với trạng thái hiện tại của object.
  • FormatException — định dạng dữ liệu không hợp lệ (ví dụ, cố gắng chuyển "abcd" thành số).

5. Ví dụ: các exception khác nhau "nổ" như thế nào

Hãy cùng xem ví dụ code gây ra các exception khác nhau cho dễ hình dung nhé.

Ví dụ 1: NullReferenceException

string? str = null;
Console.WriteLine(str.Length); // Ở đây "Bùm!" — str là null

Kết quả:

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

Ví dụ 2: DivideByZeroException

int a = 42;
int b = 0;
int c = a / b; // Nguy hiểm! Chia cho 0

Kết quả:

Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.

Ví dụ 3: IndexOutOfRangeException

int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // Không có phần tử index 5

Ở đây rõ ràng là index bị thừa, và kết quả là:

Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.

Ví dụ 4: FileNotFoundException

using System.IO;

string content = File.ReadAllText("secret.txt");

Nếu bạn không có file tên "secret.txt", bạn sẽ nhận được thông báo kiểu như sau:

Unhandled exception. System.IO.FileNotFoundException: Could not find file '/Users/zapp/RiderProjects/ConsoleApp1/ConsoleApp1/bin/Debug/net9.0/secret.txt'.

Ví dụ 5: FormatException

string input = "mười ba";
int number = int.Parse(input); // Chuỗi không phải số

Kết quả:

Unhandled exception. System.FormatException: The input string 'mười ba' was not in a correct format.

6. Làm gì với những kiến thức này?

Hiểu cách exception hoạt động thực sự rất quan trọng với bất kỳ lập trình viên nào. Đây không chỉ là "lý thuyết cho vui", mà là kỹ năng thực tế ảnh hưởng trực tiếp đến độ ổn định của chương trình. Khi bạn biết cách xử lý lỗi đúng, chương trình của bạn sẽ không "sập" ngay khi có sự cố, mà thay vào đó — sẽ báo cho người dùng một cách rõ ràng, dễ hiểu về vấn đề. Điều này làm cho chương trình của bạn đáng tin cậy, chuyên nghiệp và thân thiện hơn.

Và không chỉ là tiện lợi đâu. Khi phỏng vấn, người ta thường hỏi về cơ chế xử lý exception, các loại Exception và cách cơ chế này hoạt động trong .NET. Nắm vững chủ đề này cũng là một điểm cộng khi đi xin việc.

Chưa kể, bạn sẽ còn gặp exception rất nhiều trong các dự án thực tế. Làm việc với file, mạng, database, thư viện bên ngoài, framework — tất cả đều dùng hệ thống exception rất nhiều. Vậy nên tốt nhất là hiểu ngay từ đầu, để sau này không bị bất ngờ.

7. Những lỗi thường gặp khi làm việc với exception

Lỗi số 1: bỏ qua exception hoàn toàn.
Một số bạn mới (và đôi khi cả dev lâu năm) coi exception là thứ phiền phức. Họ hoặc là bỏ qua hoàn toàn, hoặc tệ hơn, bọc code trong catch { } rồi không làm gì cả. Kết quả là chương trình "im lặng" chạy tiếp trong trạng thái kỳ lạ, và không thể lần ra nguyên nhân lỗi.

Lỗi số 2: nhầm lẫn giữa exception và lỗi thông thường.
Nhiều khi quên rằng không phải lỗi nào cũng nên bắt bằng try-catch. Ví dụ, nếu người dùng nhập sai dữ liệu, tốt nhất là kiểm tra thủ công trước, đừng trông chờ vào FormatException và càng không nên dùng exception như logic chính. Exception là cho trường hợp ngoại lệ, không phải routine.

Lỗi số 3: hiểu sai hệ thống phân cấp exception.
Đôi khi lại bắt exception quá tổng quát (Exception) và kết quả là bắt luôn cả những lỗi đáng lẽ nên để crash. Đôi khi lại bắt quá hẹp (IndexOutOfRangeException, NullReferenceException), quên rằng còn nhiều lỗi khác có thể xảy ra. Quan trọng là phải hiểu hệ thống phân cấp exception trong .NET và biết mình muốn xử lý cái gì.

Và quan trọng nhất — nhớ rằng: exception trong C# không phải là tai nạn hay tín hiệu "toang rồi", mà chỉ là cơ chế chuyển sang kịch bản xử lý đặc biệt. Đây là công cụ mạnh nếu bạn dùng đúng cách.

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Ví dụ đơn giản về việc ném ra ngoại lệ
Ví dụ đơn giản về việc ném ra ngoại lệ
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Các exception phổ biến khác nhau
Các exception phổ biến khác nhau
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION