Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 11 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
11
Dung lượng
192,02 KB
Nội dung
XỬLÝNGOẠILỆ
· Phát sinh và bắt giữ ngoại lệ
· Câu lệnh throw
· Câu lệnh catch
· Câu lệnh finally
· Những đối tượng ngoại lệ
· Tạo riêng các ngoại lệ
· Phát sinh lại ngoại lệ
· Câu hỏi & bài tập
Ngôn ngữ C# cũng giống như bất cứ ngôn ngữ hướng đối tượng khác, cho
phép xửlý những lỗi và các điều kiện không bình thường với những ngoại lệ. Ngoại
lệ là một đối tượng đóng gói những thông tin về sự cố của một chương trình không
bình thường.
Một điều quan trọng để phân chia giữa bug, lỗi, và ngoại lệ. Một bug là một lỗ
i lập
trình có thể được sửa chữa trước khi mã nguồn được chuyển giao. Những ngoạilệ
thì không được bảo vệ và tương phản với những bug. Mặc dù một bug có thể là
nguyên nhân sinh ra ngoại lệ, chúng ta cũng không dựa vào những ngoạilệ để xửlý
những bug trong chương trình, tốt hơn
là chúng ta nên sửa chữa những bug này.
Một lỗi có nguyên nhân là do phía hành động của người sử dụng. Ví dụ, người
sử dụng nhập vào một số nhưng họ lại nhập vào ký tự chữ cái. Một lần nữa, lỗi có
thể làm xuất hiện ngoại lệ, nhưng chúng ta có thể ngăn ngừa điều này bằng cách
bắt giữ lỗi với mã hợp lệ. Những lỗi có thể được đoán trước và được ngăn ngừa.
Thậm chí nếu chúng ta xóa tất cả những bug và dự đ
oán tất cả các lỗi của người
dùng, chúng ta cũng có thể gặp phải những vấn đề không mong đợi, như là xuất
hiện trạng thái thiếu bộ nhớ (out of memory), thiếu tài nguyên hệ thống, những
nguyên nhân này có thể do các chương trành khác cùng hoạt động ảnh hưởng đến.
Chúng ta không thể ngăn ngừa các ngoạilệ này, nhưng chúng ta có thể xửlý chúng
để chúng không thể làm tổn hại đến chương trình.
Khi một chương trình gặ
p một tình huống ngoại lệ, như là thiếu bộ nhớ thì nó sẽ
tạo một ngoại lệ. Khi một ngoạilệ được tạo ra, việc thực thi của các chức năng hiện
hành sẽ bị treo cho đến khi nào việc xửlýngoạilệ tương ứng được tìm thấy.
Điều này có nghĩa rằng nếu chức năng hoạt động hiện hành không thực hiện
việc xửlýngoại lệ, thì chức năng này sẽ bị chấm dứt và hàm gọi sẽ nhận sự thay
đổi đến việc xửlýngoại lệ. Nếu hàm gọi này không thực hiện việc xửlýngoại lệ,
ngoại lệ sẽ được xửlý sớm bởi CLR, điều này d
ẫn đến chương trình của chúng ta sẽ
kết thúc.
Một trình xửlýngoạilệ là một khối lệnh chương trình được thiết kế xửlý các
ngoại lệ mà chương trình phát sinh. Xửlýngoạilệ được thực thi trong trong câu lệnh
catch. Một cách lý tưởng thì nếu một ngoạilệ được bắt và được xử lý, thì chương
trình có thể sửa chữa được vấn đề và tiếp tục thực hiệ
n hoạt động. Thậm chí nếu
chương trình không tiếp tục, bằng việc bắt
giữ ngoạilệ chúng ta có cơ hội để in ra những thông điệp có ý nghĩa và kết thúc chương
trình một cách rõ ràng.
Nếu đoạn chương trình của chúng ta thực hiện mà không quan tâm đến bất cứ
ngoại lệ nào mà chúng ta có thể gặp (như khi giải phóng tài nguyên mà chương trình
được cấp phát), chúng ta có thể đặt đoạn mã này trong khối finally
, khi đó nó sẽ chắc
chắn sẽ được thực hiện thậm chí ngay cả khi có một ngoạilệ xuất hiện.
Phát sinh và bắt giữ ngoạilệ
Trong ngôn ngữ C#, chúng ta chỉ có thể phát sinh (throw) những đối tượng các
kiểu dữ liệu là System.Exception, hay những đối tượng được dẫn xuất từ kiểu
dữ liệu này. Namespace System của CLR chứa một số các kiểu dữ liệuxửlýngoại
lệ mà chúng ta có thể sử dụng trong chương trình. Những kiểu dữ liệungoạilệ
này bao gồm ArgumentNull- Exception, InValidCastException, và
OverflowException, cũng như nhiều lớp khác nữa.
Câu lệnh throw
Để phát tín hiệu một sự không bình thường trong một lớp của ngôn ngữ C#, chúng ta
phát sinh một ngoại lệ. Để làm được điều này, chúng ta sử dụng từ khóa throw. Dòng
lệnh sau tạo
ra một thể hiện mới của System.Exception và sau đó throw nó:
throw new System.Exception();
Khi phát sinh ngoạilệ thì ngay tức khắc sẽ làm ngừng việc thực thi trong khi CLR
sẽ tìm kiếm một trình xửlýngoại lệ. Nếu một trình xử lýngoạilệ không được
tìm thấy trong phương thức hiện thời, thì CLR tiếp tục tìm trong phương thức gọi
cho đến khi nào tìm thấy. Nếu CLR trả về lớp Main() mà không tìm thấy bất cứ trình
xử lýngoạilệ nào, thì nó sẽ kết thúc chương trình.
Ví dụ 13.1: Throw ngoạ
i lệ.
namespace Programming_CSharp
{
using System;
public class
Test
{
public static void Main()
{
Console.WriteLine(“Enter
Main ”); Test t = new Test();
t.Func1();
Console.WriteLine(“Exit Main ”);
}
public void Func1()
{
Console.WriteLine(“Enter
Func1 ”); Func2();
Console.WriteLine(“Exit Func1 ”);
}
public void Func2()
{
Console.WriteLine(“Enter
Func2 ”); throw new
System.Exception();
Console.WriteLine(“Exit Func2 ”);
}
}
}
Kết quả:
Enter Main
Enter Func1
Enter Func2
Exception occurred: System.Exception: An exception of type System.Exception was
throw.
at Programming_CSharp.Test.Func2() in exception01.cs:line 26 at
Programming_CSharp.Test.Func1() in exception01.cs:line 20 at
Programming_CSharp.Test.Main() in exception01.cs:line 12
Ví dụ minh họa đơn giản này viết ra màn hình console thông tin khi nó nhập vào
trong một hàm và chuẩn bị đi ra từ một hàm. Hàm Main() tạo thể hiện mới của kiểu
Test và sau đó gọi hàm Func1(). Sau khi in thông điệp “Enter Func1”, hàm Func1()
này gọi hàm Func2(). Hàm Func2() in ra thông điệp đầu tiên và phát sinh một ngoạilệ
kiểu System.Exception. Việc thực
thi sẽ ngưng ngay tức khắc, và CLR sẽ tìm kiếm trình xử lýngoạilệ trong hàm Func2().
Do không tìm thấy ở đây, CLR tiếp tục vào stack lấy hàm đã gọi trước tức là Func1 và
tìm kiếm trình x
ử lýngoại lệ. Một lần nữa trong Func1 cũng không có đoạn xử lýngoại
lệ. Và CLR trả về hàm Main. Tại hàm Main cũng không có, nên CLR sẽ gọi trình mặc
định xửlýngoại lệ, việc này đơn giản là xuất ra một thông điệp lỗi.
Câu lệnh catch
Trong C#, một trình xử lýngoạilệ hay một đoạn chương trình xửlý các ngoạilệ được
gọi là một khối catch
và được tạo ra với từ khóa catch.
Trong ví dụ 13.2 sau, câu lệnh throw được thực thi bên trong khối try, và một khối
catch
được sử dụng để công bố rằng một lỗi đã được xử lý.
Ví dụ 13.2: bắt giữ ngoại lệ.
namespace Programming_CSharp
{
using System;
public class Test
{
public static void Main()
{
Console.WriteLine(“Enter
Main ”); Test t = new Test();
t.Func1();
Console.WriteLine(“Exit Main ”);
}
public void Func1()
{
Console.WriteLine(“Enter
Func1 ”); Func2();
Console.WriteLine(“Exit Func1 ”);
}
public void Func2()
{
Console.WriteLine(“Enter Func2 ”);
try
{
Console.WriteLine(“Entering try
block ”); throw new System.Exception();
Console.WriteLine(“Exiting try block ”);
}
catch
{
Console.WriteLine(“Exception caught and handled.”);
}
Console.WriteLine(“Exit Func2 ”);
}
}
}
Kết quả:
Enter Main
Enter Func1
Enter Func2
Entering try block Exception
caught and handled. Exit Func2
Exit Func1
Exit Main
Ví dụ 13.2 cũng tương tự như ví dụ minh họa trong 13.1 ngoại trừ chương trình
thêm vào trong một khối try/catch. Thông thường chúng ta cũng co thể đặt khối try
bao quanh những đoạn chương trình tiềm ẩn gây ra sự nguy hiểm, như là việc truy cập
file, cấp phát bộ nhớ
Theo sau khối try là câu lệnh catch tổng quát. Câu lệnh catch trong ví dụ này là tổng
quát
bởi vì chúng ta không xác định loại ngoại l
ệ nào mà chúng ta bắt giữ. Trong trường
hợp tổng quát này thì khối catch này sẽ bắt giữ bất cứ ngoạilệ nào được phát sinh.
Sử dụng câu lệnh catch để bắt giữ ngoạilệ xác định sẽ được thảo luận trong phần sau
của chương.
Trong ví dụ 13.2 này, khối catch đơn giản là thông báo ra một ngoạilệ được bắt giữ và
được xử lý. Trong ví dụ của thế giớ
i thực, chúng ta có thể đưa hành động đúng để sửa
chữa vấn đề mà gây ra sự ngoại lệ. Ví dụ, nếu người sử dụng đang cố mở một tập tin có
thuộc tính chỉ đọc, chúng ta có thể gọi một phương thức cho phép người dùng thay đổi
thuộc tính của tập tin. Nếu chương trình thực hiện thiếu bộ nhớ, chúng ta có thể phát
sinh cho người dùng cơ hội để
đóng bớt các ứng dụng khác lại. Thậm chí trong trường
hợp xấu nhất ta không khắc phục được thì khối catch này có thể in ra thông điệp lỗi để
người dùng biết.
Thử kiểm tra kỹ lại chương trình 13.2 trên, chúng ta sẽ thấy xuất hiện đoạn mã đi
vào từng hàm như Main(), Func1(), Func2(), và cả khối try. Chúng ta không bao giờ
thấy nó thoát khối lệnh try (tức là in ra thông báo “Exiting try block ”, hay thực hiện
lệnh này), mặc dù nó vẫn thoát ra theo thứ tự Func2(), Func1(), và Main(). Chuyện gì
xảy ra?
Khi một ngoạilệ được phát sinh, việc thi hành ngay lập tức sẽ bị tạm dừng và việc thi
hành sẽ được chuyển qua khối lệnh catch. Nó không bao giờ trả về luồng thực hiện
ban đầu, tức là các lệnh sau khi phát ra ngoạilệ trong khối try không được thực hiện.
Trong trường hợp này chúng ta sẽ không bao giờ nhận được thông báo “Exiting try
block ”. Khối lệnh catch xửlý
lỗi và sau đó chuy
ển việc thực thi chương trình đến các lệnh tiếp sau khối catch.
Ở đây không có việc quay lại cuộc gọi hàm trước trong stack. Ngoạilệ bây giờ được xử
lý, không có vấn đề gì xảy ra, và chương trình tiếp tục hoạt động bình thường. Điều này
trở nên rõ ràng hơn nếu chúng ta di chuyển khối try/catch lên hàm Func1 như trong ví
dụ minh họa
13.3 bên dưới.
Ví dụ 13.3: Catch trong hàm gọi.
namespace Programming_CSharp
{
using System;
public class Test
{
public static void Main()
{
Console.WriteLine(“Enter
Main ”); Test t = new Test();
t.Func1();
Console.WriteLine(“Exit Main ”);
}
public void Func1()
{
Console.WriteLine(“Enter Func1 ”);
try
{
Console.WriteLine(“Entering try
block ”); Func2();
Console.WriteLine(“Exiting try block ”);
}
catch
{
Console.WriteLine(“Exception caught and handled.”);
}
Console.WriteLine(“Exit
Func1 ”);
[...]... Console.WriteLine(“Enter Func2 ”); throw new System.Exception(); Console.WriteLine(“Exit Func2 ”); } } } Kết quả: Enter Main Enter Func1 Entering try block Enter Func2 Exception caught and handled Exit Func1 Exit Main - . thiết kế xử lý các
ngoại lệ mà chương trình phát sinh. Xử lý ngoại lệ được thực thi trong trong câu lệnh
catch. Một cách lý tưởng thì nếu một ngoại lệ được.
việc xử lý ngoại lệ, thì chức năng này sẽ bị chấm dứt và hàm gọi sẽ nhận sự thay
đổi đến việc xử lý ngoại lệ. Nếu hàm gọi này không thực hiện việc xử lý ngoại