Phát sinh lại ngoại lệ trong csharp

c-sharp

(Phạm Quyết Nghị) #1

Giả sử chúng ta muốn khối catch thực hiện một vài hành động đúng nào đó rồi sau đó phát sinh lại ngoại lệ ra bên ngoài khối catch (trong một hàm gọi). Chúng ta được phép phát sinh lại cùng một ngoại lệ hay phát sinh lại các ngoại lệ khác. Nếu phát sinh ra ngoại lệ khác, chúng ta có thể phải nhúng ngoại lệ ban đầu vào bên trong ngoại lệ mới để phương thức gọi có thể hiểu được lai lịch và nguồn gốc của ngoại lệ. Thuộc tính InnerException của ngoại lệ mới cho phép truy cập ngoại lệ ban đầu.

Bởi vì InnerException cũng là một ngoại lệ, nên nó cũng có một ngoại lệ bên trong. Do vậy, toàn bộ dây chuyền ngoại lệ là một sự đóng tổ (nest) của một ngoại lệ này với một ngoại lệ khác. Giống như là con lật đật, mỗi con chứa trong một con và đến lượt con bên trong lại chứa…

Ví dụ 13.8: Phát sinh lại ngoại lệ & ngoại lệ inner.

namespace Programming_CSharp
{
	using System;
	// tạo ngoại lệ riêng
	public class MyCustomException : System.Exception
	{
		public MyCustomException( string message, Exception inner): base(message, inner)
		{
		}
	}
		
	public class Test
	{
		public static void Main()
		{
			Test t = new Test();
			t.TestFunc();
		}
		
		// chia hai số và xử lý ngoại lệ
		public void TestFunc()
		{
			try
			{
				DangerousFunc1();
			}
			catch (MyCustomException e)
			{
				Console.WriteLine(“\n{0}”, e.Message);
				Console.WriteLine(“Retrieving exception history...”);
				Exception inner = e.InnerException;
				while ( inner != null)
				{
					Console.WriteLine(“{0}”, inner.Message);
					inner = inner.InnerException;
				}
			}
		}

		public void DangerousFunc1()
		{
			try
			{
				DangerousFunc2();
			}
			catch (System.Exception e)
			{
				MyCustomException ex = new
				MyCustomException(“E3 – Custom Exception Situation”, e);
				throw ex;
			}
		}
		
		public void DangerousFunc2()
		{
			try
			{
				DangerousFunc3();
			}
			catch (System.DivideByZeroException e)
			{
				Exception ex = new Exception(“E2 - Func2 caught divide by zero”, e);
				throw ex;
			}
		}
		
		public void DangerousFunc3()
		{
			try
			{
				DangerousFunc4();
			}
			catch (System.ArithmeticException)
			{
				throw;
			}
			catch (System.Exception)
			{
				Console.WriteLine(“Exception handled here.”);
			}
		}
		
		public void DangerousFunc4()
		{
			throw new DivideByZeroException(“E1 – DivideByZero Exception”);
		}
	}
}

Kết quả:

E3 – Custom Exception Situation!
Retrieving exception history...
E2 - Func2 caught divide by zero
E1 – DivideByZeroException

Để hiểu rõ hơn ta có thể dùng trình debugger để chạy từng bước chương trình khi đó ta sẽ hiểu rõ từng bước thực thi cũng như việc phát sinh các ngoại lệ.

Chương trình bắt đầu với việc gọi hàm DangerousFunc1() trong khối try:

try
{
	DangerousFunc1();
}

DangerousFunc1() gọi DangerousFunc2(), DangerousFunc2() lại gọi DangerousFunc3(), và cuối cùng DangerousFunc3() gọi DangerousFunc4(). Tất cả việc gọi này điều nằm trong khối try. Cuối cùng, DangerousFunc4() phát sinh ra ngoại lệ DivideByzeroException. Ngoại lệ này bình thường có chứa thông điệp bên trong nó, nhưng ở đây chúng ta tự do dùng thông điệp mới. Để dễ theo dõi chúng ta đưa vào các chuỗi xác nhận tuần tự các sự kiện diễn ra.

Ngoại lệ được phát sinh trong DangerousFunc4() và nó được bắt trong khối catch trong hàm DangerousFunc3(). Khối catch trong DangerousFunc3() sẽ bắt các ngoại lệ ArithmeticException ( như là DivideByZeroException), nó không thực hiện hành động nào mà chỉ đơn giản là phát sinh lại ngoại lệ:

catch ( System.ArithmeticException)
{
	throw;
}

Cú pháp để thực hiện phát sinh lại cùng một ngoại lệ mà không có bất cứ bổ sung hay hiệu chỉnh nào là : throw.

Do vậy ngoại lệ được phát sinh cho DangerousFunc2(), khối catch trong DangerousFunc2() thực hiện một vài hành động và tiếp tục phát sinh một ngoại lệ có kiểu mới. Trong hàm khởi dựng của ngoại lệ mới, DangerousFunc2() truyền một chuỗi thông điệp mới (“E2 - Func2 caught divide by zero”) và ngoại lệ ban đầu. Do vậy ngoại lệ ban đầu (E1) trở thành ngoại lệ bên trong của ngoại lệ mới (E2). Sau đó hàm DangerousFunc2() phát sinh ngoại lệ này (E2) cho hàm DangerousFunc1().

DangerousFunc1() bắt giữ ngoại lệ này, làm một số công việc và tạo ra một ngoại lệ mới có kiểu là MyCustomException, truyền vào hàm khởi dựng của ngoại lệ mới một chuỗi mới (“E3 – Custom Exception Situation!”) và ngoại lệ được bắt giữ (E2). Chúng ta nên nhớ rằng ngoại lệ được bắt giữ là ngoại lệ có chứa ngoại lệ DivideByZeroException (E1) bên trong nó.

Tại thời điểm này, chúng ta có một ngoại lệ kiểu MyCustomException (E3), ngoại lệ này chứa bên trong một ngoại lệ kiểu Exception (E2), và đến lượt nó chứa một ngoại lệ kiểu DivideByZeroException (E1) bên trong. Sau cùng ngoại lệ được phát sinh cho hàm TestFunc; Khi khối catch của TestFunc thực hiện nó sẽ in ra thông điệp của ngoại lệ :

E3 – Custom Exception Situation!

sau đó từng ngoại lệ bên trong sẽ được lấy ra thông qua vòng lặp while:

while ( inner != null)
{
	Console.WriteLine(“{0}”, inner.Message);
	inner = inner.InnerException;
}

Kết quả là chuỗi các ngoại lệ được phát sinh và được bắt giữ:

Retrieving exception history...
E2 - Func2 caught divide by zero
E1 – DivideByZero Exception

Tài liệu c# tiếng việt cho người mới bắt đầu (newbie)

89% Thành viên có cách giải cho một câu hỏi. Bạn thì sao?