Lúc này ngoại lệ không được xử lý bên trong hàm Func2(), mà nó được xử lý bên
trong hàm Func1(). Khi hàm Func2() được gọi, nó in câu lệnh thông báo vào hàm
rồi phát sinh một ngoại lệ. Việc thực hiện chương trình bị ngưng, CLR tìm kiếm
phần xử lý ngoại lệ, nhưng trong hàm này không có và CLR vào stack lấy hàm gọi
trong trường hợp này là Func1(). Câu lệnh catch sẽ được gọi, và việc thực thi tiếp tục
thực hiện bình thường sau câu lệnh catch.
Hãy chắc chắn rằng chúng ta đ
ã hiểu rõ tại sao câu lệnh “Exiting try block” và “Exit
Func2” không được in ra. Chúng ta có thể dùng cách cũ để kiểm tra việc này bằng
cách dùng chương trình debug cho chương trình chạy từng bước để tìm hiểu rõ hơn.
Tạo một khối catch xác định:
Cho đến bây giờ chúng ta chỉ dùng khối catch tổng quát, tức là với bất cứ ngoại lệ
nào cũng được. Tuy nhiên chúng ta có thể tạo ra khối catch xác định để xử lý chỉ mộ
t
vài các ngoại lệ chứ không phải toàn bộ ngoại lệ, dựa trên kiểu của ngoại lệ phát
sinh. Ví dụ 13.4 minh họa cách xác định loại ngoại lệ mà chúng ta xử lý.
Ví dụ13.4: Xác định ngoại lệ để bắt. namespace Programming_CSharp
{using System;
public class
Test double a = 5;
double b = 0;
Console.WriteLine(“{0} / {1} = {2}”, a, b, DoDivide(a,b));
catch (System.DivideByZeroException)
{Console.WriteLine(“DivideByZeroException caught!”);
}catch (System.ArithmeticException)
{
}
catch
return a/b;
}
}
}
Kết quả:
DivideByZeroException caught!
Trong ví dụ này, phương thức DoDivide() sẽ không cho phép chúng ta chia cho zero
bởi một số khác, và cũng không cho phép chia số zero. Nó sẽ phát sinh một đối tượng
của Divide- ByzeroException nếu chúng ta thực hiện chia với zero. Trong toán học việc
lấy zero chia cho một số khác là được phép, nhưng trong ví dụ minh họa của chúng ta
không cho phép thực hiện việc này, nếu thực hiện sẽ phát sinh ra một ngoại lệ
ArithmeticException.
Khi mộ
t ngoại lệ được phát sinh, CLR sẽ kiểm tra mỗi khối xử lý ngoại lệ theo thứ tự
và sẽ lấy khối đầu tiên thích hợp. Khi chúng ta thực hiện với a=5 và b=7 thì kết quả như
sau:
5 / 7 = 0.7142857142857143
Như chúng ta mong muốn, không có ngoại lệ được phát sinh. Tuy nhiên, khi chúng ta
thay đổi giá trị của a là 0, thì kết quả là:
trong trường hợp những ngoại lệ không đoán trước được.
Câu lệnh finally
Trong một số tình huống, việc phát sinh ngoại lệ và unwind stack có thể tạo ra một
số vấn
đề. Ví dụ như nếu chúng ta mở một tập tin hay trường hợp khác là xác nhận một tài
nguyên, chúng ta có thể cần thiết một cơ hội để đóng một tập tin hay là giải phóng
bộ nhớ đệm mà chương trình đã chiếm giữ trước đó.
Ghi chú: Trong ngôn ngữ C#, vấn đề này ít xả
y ra hơn do cơ chế thu dọn tự động
của C# ngăn ngừa những ngoại lệ phát sinh từ việc thiếu bộ nhớ.
Tuy nhiên, có một số hành động mà chúng ta cần phải quan tâm bất cứ khi nào một
ngoại
lệ được phát sinh ra, như việc đóng một tập tin, chúng ta có hai chiến lược để lựa
chọn thực hiện. Một hướng tiếp cận là đưa hành động nguy hiểm vào trong khố
i try
và sau đó thực hiện việc đóng tập tin trong cả hai khối catch và try. Tuy nhiên,
điều này gây ra đoạn chương trình không được đẹp do sử dụng trùng lắp lệnh.
Ngôn ngữ C# cung cấp một sự thay thế tốt hơn trong khối finally.
Đoạn chương trình bên trong khối catch được đảm bảo thực thi mà không quan
tâm đến việc khi nào thì một ngoại lệ được phát sinh. Phương thức TestFunc() trong
ví dụ 13.5 minh h
ọa việc mở một tập tin như là hành động đầu tiên của nó, sau đó
phương thức thực hiện một vài các phép toán toán học, và sau đó là tập tin được
đóng. Có thể trong quá trình mở tập tin cho đến khi đóng tập tin chương trình phát
sinh ra một ngoại lệ. Nếu xuất hiện ngoại lệ, và khi đó tập tin vẫn còn mở. Người
phát triển biết rằng không có chuyện gì xảy ra, và cuối của phương thức này thì t
ập
tin sẽ được đóng. Do chức năng đóng tập tin được di chuyển vào trong khối
finally, ở đây nó sẽ được thực thi mà không cần quan tâm đến việc có phát sinh
{
Console.WriteLine(“Open file here”);
double a = 5;
double b = 0;
Console.WriteLine(“{0} /{1} = {2}”, a, b,
DoDivide(a,b)); Console.WriteLine(“This line may or not
print”);
}catch (System.DivideByZeroException)
{
}
catch
{ }
{
if ( b == 0)
{throw new System.DivideByZeroException();
}if ( a == 0)
{throw new System.ArithmeticException();
}return a/b;
}
}
}
đối tượng được xây dựng cho việc xử lý ngoại lệ. Đối tượng System.Exception cung
cấp một số các phương thức và thuộc tính hữu dụng. Thuộc tính Message cung cấp
thông tin về ngoại lệ, như là lý do
tại sao ngoại lệ được phát sinh. Thuộc tính Message là thuộc tính chỉ
đọc, đoạn
chương trình phát sinh ngoại lệ có thể thiết lập thuộc tính Message như là một đối
mục cho bộ khởi dựng của ngoại lệ. Thuộc tính HelpLink cung cấp một liên kết để trợ
giúp cho các tập tin liên quan đến các ngoại lệ. Đây là thuộc tính chỉ đọc. Thuộc tính
StackTrace cũng là thuộc tính chỉ đọc
và được thiết lập bởi CLR. Trong ví dụ 13.6 thuộc tính Exception.HelpLink được
thiết l
ập và truy cập để cung cấp thông tin cho người sử dụng về ngoại lệ
DivideBy-ZeroException. Thuộc tính StackTrace của ngoại lệ được sử dụng để cung
cấp thông tin stack cho câu lệnh lỗi. Một thông tin stack cung cấp hàng loạt các cuộc
gọi stack của phương thức gọi mà dẫn đến những ngoại lệ được phát sinh.
Ví dụ 13.6: Làm việc với đối tượng ngoại lệ. namespace Programming_CSharp
{using System;
public class Test
{} Console.WriteLine(“Open file here”);
double a = 12;
double b = 0;
Console.WriteLine(“{0} /{1} = {2}”, a, b,
DoDivide(a,b)); Console.WriteLine(“This line may or not
print”);
catch (System.DivideByZeroException e)
{Console.WriteLine(“\nDivideByZeroException! Msg: {0}”,
e.Message); Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink);
Console.WriteLine(“\nHere’s a stack trace: {0}\n”, e.StackTrace);
}catch
if ( a == 0)
{throw new ArithmeticException();
}
return a/b;
}
}
}