Tải bản đầy đủ (.pdf) (12 trang)

Tài liệu XỬ LÝ NGOẠI LỆ phần 2 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (188.42 KB, 12 trang )

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






{

public static void Main()

{

Test t = new Test();

t.TestFunc();

}

// ta thử chia hai phần xử lý ngoại lệ 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ạ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à:

ArithmeticException caught!
Ngoại lệ được phát sinh, và CLR sẽ kiểm tra ngoại lệ đầ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ạ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 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ại lệ nào được xử lý bởi khối
xử lý ngoại lệ 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ại lệ
xác định trong một hàm và nhiều ngoại lệ 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ại lệ nhất. Chúng ta cũng có
thể tạo ra nhiều khối xử lý ngoại lệ chung ở trong đoạn chương trình ở mức cao

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

hay không một ngoại lệ 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ại lệ 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ại lệ

Cho đến lúc này thì chúng ta có th
ể sử dụng tốt các ngoại lệ cũng như cách xử
lý khắc phục các ngoại lệ 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ạ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

{



public static void Main()

{


Test t = new Test();

t.TestFunc();

}


// chia hai số và xử lý ngoại lệ

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 = “.v
n”;

throw e;

}



if ( a == 0)

{


throw new ArithmeticException();

}

return a/b;

}

}

}




×