1. Định nghĩa ngoại lệ và trình xử lý 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 trình xử lý ngoại lệ:
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 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ại lệ 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ại lệ xuất hiện.
Phát sinh và bắt giữ ngoại lệ
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ệu xử lý ngoại lệ mà chúng ta có thể sử dụng trong chương trình. Những kiểu dữ liệu ngoại lệ này bao gồm ArgumentNull-Exception, InValidCastException, và OverflowException, cũng như nhiều lớp khác nữa.
2. Lệnh Throw
Cú pháp: throw new System.Exception();
Khi phát sinh ngoại lệ 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ại lệ 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ại lệ nào, thì nó sẽ kết thúc chương trình.
Ví dụ:
using System;
using System.Collections.Generic;
using System.Text;
namespace Programming_CSharp
{
public class Test
{
public static void Main ()
{
Console.WriteLine("hàm Main ....");
Test t = new Test();
t.Func1();
Console.WriteLine("Kết thúc hàm Main ...");
}
public void Func1()
{
Console.WriteLine("Bắt đầu hàm Func1...");
Func2();
Console.WriteLine("Kết thúc hàm Func1...");
}
public void Func2()
{
Console.WriteLine("Bắt đầu hàm Func2...");
throw new System.Exception();
Console.WriteLine("Kết thúc hàm Func2...");
}
}
}
Giải thích ví dụ trên như sau: Hàm Main() gọi hàm Func1(). Hàm Func1() thực hiện lệnh in ra màn hình dòng “bắt đầu hàm Func1” sau đó nó gọi tới hàm Func2(). Hàm Func2() lại in ra dòng “bắt đầu hàm Func2” sau đó nó sẽ phát sinh ra một ngoại lệ dùng câu lệnh throw new System.Exception(). Tại đây chương trình bị ngừng thực thi, CLR sẽ tìm kiếm trình xử lý ngoại lệ cho ngoại lệ hàm Func2() phát sinh. CLR sẽ lần lượt tìm kiếm trong stack , ở hàm Func1() nhưng không có trình xử lý ngoại lệ nào, nó sẽ tiếp tục tìm đến hàm main nhưng ở hàm này cũng không có nên CLR sẽ gọi trình xử lý ngoại lệ mặc định, nó sẽ xuất ra một thông điệp lỗi như các bạn thấy khi thực thi chương trình.
3. Lệnh Try Catch
Trong C#, một trình xử lý ngoại lệ hay một đoạn chương trình xử lý các ngoại lệ được gọi là một khối catch và được tạo ra với từ khóa catch.
Chúng ta sẽ viết lại ví dụ trên nhưng đặt throw vào trong khối try và một khối catch sẽ dùng để xử lý ngoại lệ do lệnh throw phát sinh. Khối catch sẽ đưa ra thông báo là đã có một lỗi được xử lý.
using System;
using System.Collections.Generic;
using System.Text;
namespace Programming_CSharp
{
public class Test
{
public static void Main ()
{
Console.WriteLine("hàm Main ....");
Test t = new Test();
t.Func1();
Console.WriteLine("Kết thúc hàm Main ...");
Console.ReadLine();
}
public void Func1()
{
Console.WriteLine("Bắt đầu hàm Func1...");
Func2();
Console.WriteLine("Kết thúc hàm Func1...");
}
public void Func2()
{
Console.WriteLine("Bắt đầu hàm Func2...");
try
{
Console.WriteLine("Bắt đầu Khối try");
throw new System.Exception();
Console.WriteLine("Kết thúc khối try");
}
catch
{
Console.WriteLine("Ngoại lệ đã được xử lý");
}
Console.WriteLine("Kết thúc hàm Func2...");
}
}
}
Tương tự như ví dụ tôi đã vừa trình bày, cho đến khi chương trình thực hiện hàm Func2() khi lệnh throw phát sinh ra ngoại lệ, chương trình sẽ bị ngừng thực hiện và CLR sẽ tìm phần xử lý ngoại lệ trong stack, đầu tiên nó sẽ gọi đến hàm Func1() tại đây hàm Func2() được gọi và nó sẽ tìm thấy phần xử lý ngoại lệ trong khối catch , nó sẽ in ra dòng “Ngoại lệ đã được xử lý”. Đó cũng là lý do mà chương trình sẽ không bao giờ in ra dòng “Kết thúc khối try”.
4. Lệnh Finally
Trong một số tình huống chúng ta cần phải thực hiện bất cứ khi nào một ngoại lệ được phát sinh ra, ví dụ như việc đóng một tập tin. Để làm việc này chúng ta có thể đặt câu lệnh trong cả hai khối try và catch. Tuy nhiên có một cách giải quyết tốt hơn, đó là sử dụng câu lệnh Finnally.
Các hành động đặt trong khối finnally sẽ luôn được thực hiện mà không cần quan tâm tới việc có hay không một ngoại lệ phát sinh trong chương trình.
Chúng ta cùng xét ví dụ sau:
using System;
namespace Programming_CSharp
{
public class Test
{
public static void Main ()
{
Test t = new Test();
t.TestFunc();
Console.ReadLine();
}
// chia hai số và xử lý ngoại lệ nếu có
public void TestFunc()
{
try
{
Console.WriteLine("mở file");
double a = 5;
double b = 0;
Console.WriteLine("{0} /{1} = {2}", a, b, DoDivide(a,b));
Console.WriteLine("dòng này có thể xuất hiện hoặc không");
}
catch (System.DivideByZeroException)
{
Console.WriteLine("lỗi chia cho 0!");
}
catch
{
Console.WriteLine("không có ngoại lệ");
}
finally
{
Console.WriteLine("Đóng tệp.");
}
}
// 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;
}
}
}
Đầu tiên hãy gán a= 5 và b=0 chạy chương trình Bạn sẽ thấy lệnh Console.WriteLine("dòng này có thể xuất hiện hoặc không");
Sẽ không được thực hiện do xuất hiện một ngoại lệ là lỗi chia cho 0 và chương trình sẽ tìm tới phần xử lý ngoại lệ này
mà bỏ qua phần lệnh tiếp theo.
mà bỏ qua phần lệnh tiếp theo.
Sau đó bạn thay đổi giá trị b=12 và chạy chương trình thì lệnh
Console.WriteLine("dòng này có thể xuất hiện hoặc không"); được thực hiện.
Tuy nhiên ở cả 2 trường hợp bạn đề thấy thực hiện lệnh Console.WriteLine("Đóng tệp.");
Đó là vì lệnh này đã được đặt trong khối Finally.
1 comments:
Bài viết thật hữu ích.
Đăng nhận xét