Phát sinh và bắt giữ ngoại lệ

16 180 0
Phát sinh và bắt giữ ngoại lệ

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Phát sinh bắt giữ ngoại lệ Phát sinh bắt giữ ngoại lệ Bởi: Khuyet Danh Một điều quan trọng để phân chia bug, lỗi, ngoại lệ Một bug lỗi lập trình sửa chữa trước mã nguồn chuyển giao Những ngoại lệ không bảo vệ tương phản với bug Mặc dù bug nguyên nhân sinh ngoại lệ, không dựa vào ngoại lệ để xử lý bug chương trình, tốt nên sửa chữa bug Một lỗi có nguyên nhân phía hành động người sử dụng Ví dụ, người sử dụng nhập vào số họ lại nhập vào ký tự chữ Một lần nữa, lỗi làm xuất ngoại lệ, ngăn ngừa điều cách bắt giữ lỗi với mã hợp lệ Những lỗi đoán trước ngăn ngừa Thậm chí xóa tất bug dự đoán tất lỗi người dùng, gặp phải vấn đề không mong đợi, xuất trạng thái thiếu nhớ (out of memory), thiếu tài nguyên hệ thống, nguyên nhân chương trành khác hoạt động ảnh hưởng đến Chúng ta ngăn ngừa ngoại lệ này, xử lý chúng để chúng làm tổn hại đến chương trình Khi chương trình gặp tình ngoại lệ, thiếu nhớ tạo ngoại lệ Khi ngoại lệ tạo ra, việc thực thi chức hành bị treo việc xử lý ngoại lệ tương ứng tìm thấy Điều có nghĩa chức hoạt động hành không thực việc xử lý ngoại lệ, chức bị chấm dứt hàm gọi nhận thay đổi đến việc xử lý ngoại lệ Nếu hàm gọi không thực việc xử lý ngoại lệ, ngoại lệ xử lý sớm CLR, điều dẫn đến chương trình kết thúc Một trình xử lý ngoại lệ khối lệnh chương trình thiết kế xử lý ngoại lệ mà chương trình phát sinh Xử lý ngoại lệ thực thi trong câu lệnh catch Một cách lý tưởng ngoại lệ bắt xử lý, chương trình sửa chữa vấn đề tiếp tục thực hoạt động Thậm chí chương trình không tiếp tục, việc bắt giữ ngoại lệ có hội để in thông điệp có ý nghĩa kết thúc chương trình cách rõ ràng 1/16 Phát sinh bắt giữ ngoại lệ Nếu đoạn chương trình thực mà không quan tâm đến ngoại lệ mà gặp (như giải phóng tài nguyên mà chương trình cấp phát), đặt đoạn mã khối finally, chắn thực chí có ngoại lệ xuất Trong ngôn ngữ C#, phát sinh (throw) đối tượng kiểu liệu System.Exception, hay đối tượng dẫn xuất từ kiểu liệu Namespace System CLR chứa số kiểu liệu xử lý ngoại lệ mà sử dụng chương trình Những kiểu liệu ngoại lệ bao gồm ArgumentNull- Exception, InValidCastException, OverflowException, nhiều lớp khác Câu lệnh throw Để phát tín hiệu không bình thường lớp ngôn ngữ C#, phát sinh ngoại lệ Để làm điều này, sử dụng từ khóa throw Dòng lệnh sau tạo thể System.Exception sau throw nó: throw new System.Exception(); Khi phát sinh ngoại lệ tức khắc làm ngừng việc thực thi CLR tìm kiếm trình xử lý ngoại lệ Nếu trình xử lý ngoại lệ không tìm thấy phương thức thời, CLR tiếp tục tìm phương thức gọi tìm thấy Nếu CLR trả lớp Main() mà không tìm thấy trình xử lý ngoại lệ nào, kết thúc chương trình Throw ngoại lệ namespace Programming_CSharp { using System; public class Test { public static void Main() { 2/16 Phát sinh bắt giữ ngoại lệ 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 3/16 Phát sinh bắt giữ ngoại lệ 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 Programming_CSharp.Test.Func1() in exception01.cs:line Programming_CSharp.Test.Main() in exception01.cs:line 12 26 20 at at Ví dụ minh họa đơn giản viết hình console thông tin nhập vào hàm chuẩn bị từ hàm Hàm Main() tạo thể kiểu Test sau gọi hàm Func1() Sau in thông điệp “Enter Func1”, hàm Func1() gọi hàm Func2() Hàm Func2() in thông điệp phát sinh ngoại lệ kiểu System.Exception Việc thực thi ngưng tức khắc, CLR tìm kiếm trình xử lý ngoại lệ 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 Func1 tìm kiếm trình xử lý ngoại lệ Một lần Func1 đoạn xử lý ngoại lệ Và CLR trả hàm Main Tại hàm Main không có, nên CLR gọi trình mặc định xử lý ngoại lệ, việc đơn giản xuất thông điệp lỗi Câu lệnh catch Trong C#, trình xử lý ngoại lệ hay đoạn chương trình xử lý ngoại lệ gọi khối catch tạo với từ khóa catch Trong ví dụ sau, câu lệnh throw thực thi bên khối try, khối catch sử dụng để công bố lỗi xử lý Bắt giữ ngoại lệ namespace Programming_CSharp { using System; public class Test 4/16 Phát sinh bắt giữ ngoại lệ { 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 "); 5/16 Phát sinh bắt giữ ngoại lệ } 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ụ tương tự ví dụ minh họa trước ngoại trừ chương trình thêm vào khối try/catch Thông thường co thể đặt khối try bao quanh đoạn chương trình tiềm ẩn gây nguy hiểm, việc truy cập file, cấp phát nhớ Theo sau khối try câu lệnh catch tổng quát Câu lệnh catch ví dụ tổng quát không xác định loại ngoại lệ mà bắt giữ Trong trường hợp tổng quát khối catch bắt giữ ngoại lệ phát sinh Sử dụng câu lệnh catch để bắt giữ ngoại lệ xác định thảo luận phần sau chương Trong ví dụ này, khối catch đơn giản thông báo ngoại lệ bắt giữ xử lý Trong ví dụ giới thực, đưa hành động để sửa chữa vấn đề mà gây ngoại lệ Ví dụ, người sử dụng cố mở tập tin có thuộc tính đọc, gọi phương thức cho phép người dùng thay đổi thuộc tính tập tin Nếu chương trình thực thiếu nhớ, phát sinh cho 6/16 Phát sinh bắt giữ ngoại lệ người dùng hội để đóng bớt ứng dụng khác lại Thậm chí trường hợp xấu ta không khắc phục khối catch in thông điệp lỗi để người dùng biết Thử kiểm tra kỹ lại chương trình trên, thấy xuất đoạn mã vào hàm Main(), Func1(), Func2(), khối try Chúng ta không thấy thoát khối lệnh try (tức in thông báo “Exiting try block ”, hay thực lệnh này), thoát theo thứ tự Func2(), Func1(), Main() Chuyện xảy ra? Khi ngoại lệ phát sinh, việc thi hành bị tạm dừng việc thi hành chuyển qua khối lệnh catch Nó không trả luồng thực ban đầu, tức lệnh sau phát ngoại lệ khối try không thực Trong trường hợp không nhận thông báo “Exiting try block ” Khối lệnh catch xử lý lỗi sau chuyển việc thực thi chương trình đến lệnh tiếp sau khối catch Ở việc quay lại gọi hàm trước stack Ngoại lệ xử lý, vấn đề xảy ra, chương trình tiếp tục hoạt động bình thường Điều trở nên rõ ràng di chuyển khối try/catch lên hàm Func1 ví dụ minh họa bên Catch hàm gọi namespace Programming_CSharp { using System; public class Test { public static void Main() { Console.WriteLine("Enter Main "); Test t = new Test(); 7/16 Phát sinh bắt giữ ngoại lệ 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 "); } public void Func2() { Console.WriteLine("Enter Func2 "); throw new System.Exception(); 8/16 Phát sinh bắt giữ ngoại lệ Console.WriteLine("Exit Func2 "); } } } Kết quả: Enter Main Enter Func1 Entering try block Enter Func2 Exception caught and handled Exit Func1 Exit Main Lúc ngoại lệ không xử lý bên hàm Func2(), mà xử lý bên hàm Func1() Khi hàm Func2() gọi, in câu lệnh thông báo vào hàm phát sinh ngoại lệ Việc thực chương trình bị ngưng, CLR tìm kiếm phần xử lý ngoại lệ, hàm CLR vào stack lấy hàm gọi trường hợp Func1() Câu lệnh catch gọi, việc thực thi tiếp tục thực bình thường sau câu lệnh catch Hãy chắn hiểu rõ câu lệnh “Exiting try block” “Exit Func2” không in Chúng ta dùng cách cũ để kiểm tra việc cách dùng chương trình debug cho chương trình chạy bước để tìm hiểu rõ Tạo khối catch xác định: Cho đến dùng khối catch tổng quát, tức với ngoại lệ Tuy nhiên tạo khối catch xác định để xử lý vài ngoại lệ toàn ngoại lệ, dựa kiểu ngoại lệ phát sinh Ví dụ sau minh họa cách xác định loại ngoại lệ mà xử lý Xác định ngoại lệ để bắt namespace Programming_CSharp 9/16 Phát sinh bắt giữ ngoại lệ { 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) 10/16 Phát sinh bắt giữ ngoại lệ { Console.WriteLine("ArithmeticException caught!"); } catch { Console.WriteLine("Unknown exception caught"); } } // thực 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! 11/16 Phát sinh bắt giữ ngoại lệ -Trong ví dụ này, phương thức DoDivide() không cho phép chia cho zero số khác, không cho phép chia số zero Nó phát sinh đối tượng Divide- ByzeroException thực chia với zero Trong toán học việc lấy zero chia cho số khác phép, ví dụ minh họa không cho phép thực việc này, thực phát sinh ngoại lệ ArithmeticException Khi ngoại lệ phát sinh, CLR kiểm tra khối xử lý ngoại lệ theo thứ tự lấy khối thích hợp Khi thực với a=5 b=7 kết sau: / = 0.7142857142857143 Như mong muốn, ngoại lệ phát sinh Tuy nhiên, thay đổi giá trị a 0, kết là: ArithmeticException caught! Ngoại lệ phát sinh, CLR kiểm tra ngoại lệ đầu tiên: DivideByZeroException Bởi không phù hợp, nên tiếp tục tìm khối xử lý ArithmeticException chọn Cuối cùng, giả sử thay đổi giá trị b Khi thực điều dẫn đến ngoại lệ DivideByZeroException Chúng ta phải cẩn thận thứ tự câu lệnh catch, DivideByZero- Exception dẫn xuất từ ArithmeticException Nếu đảo thứ tự câu lệnh catch, ngoại lệ DivideByZeroException phù hợp với khối xử lý ngoại lệ ArithmeticException Và việc xử lý ngoại lệ không giao cho khối xử lý DivideByZeroException Thật vậy, thứ tự đảo, không cho phép ngoại lệ xử lý khối xử lý ngoại lệ DivideByZeroException Trình biên dịch nhận DivideByZeroException không thực thông báo lỗi biên dịch Chúng ta phân phối câu lệnh try/ catch, cách bắt giữ ngoại lệ xác định hàm nhiều ngoại lệ tổng quát nhiều hàm Mục đích thực đưa thiết kế Giả sử có phương thức A, phương thức gọi phương thức khác tên phương thức B, đến lượt 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 phương thức D gọi phương thức E Phương thức E mức độ sâu chương trình chúng ta, phương thức A, B mức độ cao Nếu đoán trước phương thức E phát sinh ngoại lệ, tạo khối try/catch để bắt giữ ngoại lệ 12/16 Phát sinh bắt giữ ngoại lệ chỗ gần nơi phát sinh ngoại lệ Chúng ta tạo nhiều khối xử lý ngoại lệ chung đoạn chương trình mức cao trường hợp ngoại lệ không đoán trước Câu lệnh finally Trong số tình huống, việc phát sinh ngoại lệ unwind stack tạo số vấn đề Ví dụ mở tập tin hay trường hợp khác xác nhận tài nguyên, cần thiết hội để đóng tập tin giải phóng nhớ đệm mà chương trình chiếm giữ trước Trong ngôn ngữ C#, vấn đề xảy chế thu dọn tự động C# ngăn ngừa ngoại lệ phát sinh từ việc thiếu nhớ Tuy nhiên, có số hành động mà cần phải quan tâm ngoại lệ phát sinh ra, việc đóng tập tin, có hai chiến lược để lựa chọn thực Một hướng tiếp cận đưa hành động nguy hiểm vào khối try sau thực việc đóng tập tin hai khối catch try Tuy nhiên, điều gây đoạn chương trình không đẹp sử dụng trùng lắp lệnh Ngôn ngữ C# cung cấp thay tốt khối finally Đoạn chương trình bên khối catch đảm bảo thực thi mà không quan tâm đến việc ngoại lệ phát sinh Phương thức TestFunc() ví dụ 13.5 minh họa việc mở tập tin hành động nó, sau phương thức thực vài phép toán toán học, sau tập tin đóng Có thể trình mở tập tin đóng tập tin chương trình phát sinh ngoại lệ Nếu xuất ngoại lệ, tập tin mở Người phát triển biết chuyện xảy ra, cuối phương thức tập tin đóng Do chức đóng tập tin di chuyển vào khối finally, thực thi mà không cần quan tâm đến việc có phát sinh hay không ngoại lệ chương trình Sử dụng khối finally namespace Programming_CSharp { using System; public class Test 13/16 Phát sinh bắt giữ ngoại lệ { public static void Main() { Test t = new Test(); t.TestFunc(); } // chia hai số xử lý ngoại lệ 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) { Console.WriteLine("DivideByZeroException caught!"); } catch 14/16 Phát sinh bắt giữ ngoại lệ { Console.WriteLine("Unknown exception caught"); } Finally { Console.WriteLine("Close file here."); } } // thực 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; } } 15/16 Phát sinh bắt giữ ngoại lệ } Kết quả: Open file here DivideByZeroException caught! Close file here Kết trường hợp b = 12 Open file here 5/ 12 = 0.416666666666 Close file here Trong ví dụ khối catch loại bỏ thêm vào khối finally Bất ngoại lệ có phát sinh hay không khối lệnh bên finally thực thi Do nên hai trường hợp ta thấy xuất thông điệp “Close file here” 16/16 [...]... 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 12/16 Phát sinh và bắt giữ ngoại lệ ở 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... 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... } catch 14/16 Phát sinh và bắt giữ ngoại lệ { Console.WriteLine("Unknown exception caught"); } Finally { 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; } } 15/16 Phát sinh và bắt giữ ngoại lệ } ... đượ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 Sử dụng khối finally namespace Programming_CSharp { using System; public class Test 13/16 Phát sinh và bắt giữ ngoại lệ { 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... 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ệ ArithmeticException 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... 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,... chiếm giữ trước đó 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à. .. 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 Chúng ta phải cẩn thận thứ tự của câu lệnh catch, bởi vì DivideByZero-... 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... a/b; } } } Kết quả: DivideByZeroException caught! 11/16 Phát sinh và bắt giữ ngoại lệ -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 ... phương thức E phát sinh ngoại lệ, tạo khối try/catch để bắt giữ ngoại lệ 12/16 Phát sinh bắt giữ ngoại lệ chỗ gần nơi phát sinh ngoại lệ Chúng ta tạo nhiều khối xử lý ngoại lệ chung đoạn chương... với ngoại lệ Tuy nhiên tạo khối catch xác định để xử lý vài ngoại lệ toàn ngoại lệ, dựa kiểu ngoại lệ phát sinh Ví dụ sau minh họa cách xác định loại ngoại lệ mà xử lý Xác định ngoại lệ để bắt. .. quát Câu lệnh catch ví dụ tổng quát không xác định loại ngoại lệ mà bắt giữ Trong trường hợp tổng quát khối catch bắt giữ ngoại lệ phát sinh Sử dụng câu lệnh catch để bắt giữ ngoại lệ xác định

Ngày đăng: 31/12/2015, 21:47

Mục lục

  • Phát sinh và bắt giữ ngoại lệ

Tài liệu cùng người dùng

Tài liệu liên quan