Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 12 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
12
Dung lượng
188,42 KB
Nội dung
Lúc này ngoạilệ 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ạilệ
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ạilệ chứ không phải toàn bộ ngoại lệ, dựa trên kiểu của ngoạilệ phát
sinh. Ví dụ 13.4 minh họa cách xác định loại ngoạilệ mà chúng ta xử lý.
Ví dụ13.4: Xác định ngoạilệ để bắt.
namespace Programming_CSharp
{
using System;
public class
Test
{
public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// ta thử chia hai phần xửlýngoạilệ riêng
public void TestFunc()
{
try
{
}
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
{
}
Console.WriteLine(“ArithmeticException caught!”);
Console.WriteLine(“Unknown exception caught”);
}
// thực hiện phép chia hợp lệ
public double DoDivide(double a, double b)
{
if ( b == 0)
throw new
System.DivideByZeroException();
if ( a == 0)
throw new System.ArithmeticException();
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ạilệ
ArithmeticException.
Khi mộ
t ngoạilệ được phát sinh, CLR sẽ kiểm tra mỗi khối xửlýngoạilệ 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ạilệ đượ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à:
ArithmeticException caught!
Ngoại lệ được phát sinh, và CLR sẽ kiểm tra ngoạilệ đầu tiên:
DivideByZeroException. Bởi vì không phù hợp, nên nó sẽ tiếp tụ
c đi tìm và khối xửlý
ArithmeticException được chọn.
Cuối cùng, giả sử chúng ta thay đổi giá trị của b là 0. Khi thực hiện điều này sẽ dẫn đến
ngoại
lệ DivideByZeroException.
Ghi chú: Chúng ta phải cẩn thận thứ tự của câu lệnh catch, bởi vì
DivideByZero- Exception được dẫn xuất từ ArithmeticException. Nếu chúng ta
đảo thứ tự của câu lệnh catch, thì ngoạilệ DivideByZeroException sẽ được phù
hợp với kh
ối xửlýngoạilệ Arith- meticException. Và việc xửlýngoạilệ sẽ
không bao giờ được giao cho khối xửlý DivideByZeroException. Thật vậy, nếu
thứ tự này được đảo, nó sẽ không cho phép bất cứ ngoạilệ nào được xửlý bởi khối
xử lýngoạilệ DivideByZeroException. Trình biên dịch sẽ nhận ra rằng
DivideByZeroException không được thực hiện bất cứ khi nào và nó sẽ thông báo
một lỗi biên dịch.
Chúng ta có thể phân phối câu lệ
nh try/ catch, bằng cách bắt giữ những ngoạilệ
xác định trong một hàm và nhiều ngoạilệ tổng quát trong nhiều hàm. Mục đích của
thực hiện này là đưa ra các thiết kế đúng. Giả sử chúng ta có phương thức A, phương
thức này gọi một phương thức khác tên là phương thức B, đến lượt mình phương thức
B gọi phương thức C. Và phương thức C tiếp tục gọi phương thức D, cuối cùng
phương thức D gọi phương thức E. Phương thức E ở mức độ sâu nhất trong chương
trình của chúng ta, phương thức A, B ở mức độ cao hơn. Nếu chúng ta đoán trước
phương thức E có thể phát sinh ra ngoại lệ, chúng ta có thể tạo ra khối try/catch để
bắt giữ những ngoại lệ
này ở chỗ gần nơi phát sinh ra ngoạilệ nhất. Chúng ta cũng có
thể tạo ra nhiều khối xửlýngoạilệ chung ở trong đoạn chương trình ở mức cao
trong trường hợp những ngoạilệ 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ạilệ 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ạilệ 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ạilệ đượ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
hay không một ngoạilệ trong chương trình.
Ví dụ 13.5: Sử dụng khối finally.
namespace Programming_CSharp
{
using System;
public class Test
{
public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// chia hai số và xử lýngoạilệ nếu có public void TestFunc()
{
try
{
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
{
}
finall
y
{
}
}
Console.WriteLine(“DivideByZeroException caught!”);
Console.WriteLine(“Unknown exception caught”);
Console.WriteLine(“Close file here.”);
// thực hiện chia nếu hợp lệ
public double DoDivide(double a, double b)
{
if ( b == 0)
{
throw new System.DivideByZeroException();
}
if ( a == 0)
{
throw new System.ArithmeticException();
}
return a/b;
}
}
}
K
ế
t quả:
Open file here
DivideByZeroException caught!
Close file here.
Kết quả trong trường hợp b = 12
Open file here
5/ 12 = 0.416666666666
Close file here
Trong ví dụ này một khối catch được loại bỏ và thêm vào khối finally. Bất cứ khi một
ngoại
lệ có được phát sinh ra hay không thì khối lệnh bên trong finally cũng được thực thi. Do
vậy nên trong cả hai trường hợp ta cũng thấy xuất hiện thông điệp “Close file here”.
Những đối tượng ngoạilệ
Cho đến lúc này thì chúng ta có th
ể sử dụng tốt các ngoạilệ cũng như cách xử
lý khắc phục các ngoạilệ này. Trong phần này chúng ta sẽ tiến hành việc tìm hiểu các
đố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ạilệ đượ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ạilệ 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ạilệ
DivideBy-ZeroException. Thuộc tính StackTrace của ngoạilệ đượ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ạilệ đượ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
{
public static void Main()
{
Test t = new Test();
t.TestFunc();
}
// chia hai số và xử lýngoạilệ
[...]...public void TestFunc() { try { 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 { Console.WriteLine(“Unknown exception caught”); } } // thực hiện phép chia hợp lệ public double DoDivide( double a, double b) { if ( b == 0) { DivideByZeroException e = new DivideByZeroException(); e.HelpLink = “http://www.hcmunc.edu.vn”; throw e; } if ( a == 0) { throw new ArithmeticException(); . tự của câu lệnh catch, thì ngoại lệ DivideByZeroException sẽ được phù
hợp với kh
ối xử lý ngoại lệ Arith- meticException. Và việc xử lý ngoại lệ sẽ
không. cho khối xử lý DivideByZeroException. Thật vậy, nếu
thứ tự này được đảo, nó sẽ không cho phép bất cứ ngoại lệ nào được xử lý bởi khối
xử lý ngoại lệ DivideByZeroException.