Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 29 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
29
Dung lượng
1,39 MB
Nội dung
Ngơn Ngữ Lập Trình C# myFile.Close(); } } } } Kết quả: Dang doc tap tin Doc xong! Với ứng dụng này, đọc liệu mà viết ví dụ trước Trong ví dụ tạo luồng FileStream Lúc này, mode thao tác tập tin sử dụng mode FileMode.Open Sau thực việc gắn luồng với luồng BinaryReader dòng tiếp sau, luồng giúp cho đọc thông tin nhị phân: FileStream myFile = new FileStream( args[0], FileMode.Open); BinaryReader brFile = new BinaryReader(myFile); Sau tạo luồng giúp cho việc đọc thông tin nhị phân từ tập tin, chương trình bắt đầu đọc thơng qua vịng lặp: while (brFile.PeekChar() != -1) { Console.Write(“”, brFile.ReadInt32()); } Ở có vài khác nhỏ, phương thức PeekChar lớp BinaryReader sử dụng Phương thức lấy ký tự luồng Nếu ký tự cuối tập tin giá trị -1 trả Ngược lại, ký tự trả Khi ký tự khơng phải ký tự cuối tập tin lệnh bên vòng lặp đọc số integer từ đối tượng BinaryStream brFile 361 Các Lớp Cơ Sở NET Ngơn Ngữ Lập Trình C# Phương thức sử dụng để đọc số nguyên ReadInt32, sử dụng kiểu tên Framework tốt kiểu C# đưa Nên nhớ rằng, tất lớp từ Framework điều gọi ngôn ngữ C# chúng phận ngôn ngữ C# Những lớp sử dụng tốt ngơn ngữ khác C# Ngồi lớp BinaryReader cịn có phương thức khác để thực việc đọc kiểu liệu khác Những phương thức đọc sử dụng với cách mà ReadInt32 sử dụng chương trình Bảng 12.4 sau liệt kê số phương thức dùng để đọc kiểu liệu Phương thức Ý nghĩa Read Đọc ký tự chuyển vị trí đọc sang vị trí Phương thức nạp chồng gồm phương thức Đọc giá trị boolean từ luồng thời chuyển vị trí đọc sang byte ReadBoolean ReadByte ReadBytes ReadChar ReadChars ReadDecimal ReadDouble ReadInt16 ReadInt32 ReadInt64 ReadSByte ReadSingle ReadString ReadUInt16 Đọc byte từ luồng thời chuyển vị trí đọc sang byte Đọc n byte từ luồng thời sang mảng byte chuyển vị trí đọc sang n byte Đọc vị trí luồng hành chuyển vị trí đọc luồng theo sau sử dụng mã hóa ký tự xác định đọc từ luồng Đọc n ký tự từ luồng hành vào mảng n ký tự Và chuyển vị trí đọc luồng theo sau sử dụng mã hóa ký tự xác định đọc từ luồng Đọc giá trị decimal chuyển vị trí đọc sang 16 byte Đọc giá trị thực byte chuyển vị trí đọc sang byte Đọc giá trị byte integer có dấu chuyển vị trí đọc sang byte Đọc giá trị byte integer có dấu chuyển vị trí đọc sang byte Đọc giá trị byte integer có dấu chuyển vị trí đọc sang byte Đọc signed byte từ luồng chuyển vị trí đọc sang byte Đọc giá trị thực byte từ luồng chuyển vị trí đọc sang byte Đọc chuỗi từ luồng Chuỗi cố định chiều dài trước Và mã hóa lần số nguyên bit Đọc giá trị 2-byte unsigned integer từ luồng Sử dụng mã hóa thứ tự nhỏ cuối (little endian encoding) Và chuyển vị trí hành sang byte 362 Các Lớp Cơ Sở NET Ngôn Ngữ Lập Trình C# Đọc 8-byte unsigned integer từ luống hành chuyển sang byte ReadUInt64 Bảng 12.4: Các phương thức đọc BinaryReader Câu hỏi trả lời Câu hỏi 1: Các ngôn ngữ hỗ trợ NET phải tuân thủ theo quy tắc không? Trả lời 1: Như trình bày bên trên, ngơn ngữ NET phải tuân thủ theo quy định chung để hoạt động NET Những quy định gọi Common Language Specification (CLS) CLS đưa kiểu liệu chung tập luật để thao tác kiểu liệu này, CLS cho phép tạo môi trường thực thi chung mà không cần quan tâm đến ngôn ngữ sử dụng Lợi ích CLS mã nguồn viết thống để quản lý, mã nguồn viết ngơn ngữ sử dụng ngơn ngữ khác Câu hỏi 2: Nếu muốn tìm hiểu lớp cung cấp NET cách chi tiết phải tìm đâu? Trả lời 2: Để tìm hiểu chi tiết lớp NET tìm thư viện trực tuyến Microsoft có tên MSDN Online, thư viện chứa tất thông tin liên quan đến NET Framework mà người học cần quan tâm Thư viện thường xuyên cập nhật chứa thông tin phiên NET Câu hỏi thêm Câu hỏi 1: Để truy xuất thời gian đồng hồ hệ thống phải dùng lớp nào? Câu hỏi 2: Thơng tin máy tính truy xuất thông qua lớp nào? Câu hỏi 3: Tham số dịng lệnh gì? Làm để lấy tham số dòng lệnh? Câu hỏi 4: Lớp thao tác phép toán học bản? Chúng ta tạo thể lớp hay khơng? Câu hỏi 5: Lớp thao tác tập tin File chứa namespace nào? Các thao tác thực tập tin? Câu hỏi 6: Lớp cung cấp thơng tin tập tin? Các phương thức lớp này? Câu hỏi 7: Luồng gì? Phân biệt tập tin luồng? Câu hỏi 8: Có cách thức tạo tập tin? Cho biết thứ tự đọc tập tin? Câu hỏi 9: Sự khác lớp File FileInfo? Khi sử dụng lớp File tốt sử dụng FileInfo? Câu hỏi 10: Khi tạo tập tin trùng với tên tập tin cũ vị trí thư mục chuyện xảy ra? Câu hỏi 11: Nếu muốn viết liệu định dạng kiểu số dùng cách viết vào tập tin dạng nào? Bài tập 363 Các Lớp Cơ Sở NET Ngơn Ngữ Lập Trình C# Bài tập 1: Viết chương trình minh họa việc truy xuất thơng tin hệ thống máy tính sử dụng Thơng tin bao gồm: tên máy tính, hệ điều hành, nhớ, đĩa cứng Bài tập 2: Viết chương trình minh họa máy tính cá nhân cho phép thực phép tốn Chương trình menu lệnh lệnh gán cho số: cơng số 1, trừ số 2, nhân 3, Cho phép người dùng chọn lệnh thông qua nhập vào số tương ứng Sau cho người dùng nhập vào toán hạng thực phép tốn cuối in kết hình Bài tập 3: Viết chương trình cho phép xem thơng tin tập tin Chương trình cho người dùng nhập vào tên tập tin sau hiển thị thơng tin như: thuộc tính tập tin, ngày tạo lập, kích thước tập tin Bài tập 4: Viết chương trình xem tập tin văn giống lệnh type DOS Chương trình cho phép người dùng nhập tên tập tin thông qua tham số dịng lệnh Nếu người dùng khơng nhập qua tham số dịng lệnh u cầu nhập vào Bài tập 5: Viết chương trình cho phép người dùng nhập vào mảng số nguyên Sau xếp mảng theo thứ tự tăng dần lưu mảng vào tập tin đĩa với dạng nhị phân 364 Các Lớp Cơ Sở NET Ngơn Ngữ Lập Trình C# Chương 13 XỬ LÝ NGOẠI LỆ Phát sinh bắt giữ ngoại lệ Câu lệnh catch Câu lệnh throw Câu lệnh finally Những đối tượng ngoại lệ Tạo riêng ngoại lệ Phát sinh lại ngoại lệ Câu hỏi & tập Ngôn ngữ C# giống ngôn ngữ hướng đối tượng khác, cho phép xử lý lỗi điều kiện khơng bình thường với ngoại lệ Ngoại lệ đối tượng đóng gói thơng tin cố chương trình khơng bình thường 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 đố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 khơng thể 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 365 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# Đ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 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 Phát sinh bắt giữ ngoại lệ 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 ArgumentNullException, 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 Ví dụ 13.1: Throw ngoại lệ namespace Programming_CSharp { using System; public class Test { 366 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# 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 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 367 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# trình xử lý ngoại lệ Một lần Func1 khơng có đ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ụ 13.2 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ý 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 ”); } 368 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# 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 tương tự ví dụ minh họa 13.1 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 qt 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ụ 13.2 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 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 13.2 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 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? 369 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# 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 Ở khơng có việc quay lại gọi hàm trước stack Ngoại lệ xử lý, khơng có 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 13.3 bên Ví dụ 13.3: 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(); 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 ”); 370 Xử Lý Ngoại Lệ Ngôn Ngữ Lập Trình C# { 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 { 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; } } } Kết quả: Open file here 375 Xử Lý Ngoại Lệ Ngôn Ngữ Lập Trình C# 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” Những đối tượng ngoại lệ Cho đến lúc sử dụng tốt ngoại lệ cách xử lý khắc phục ngoại lệ Trong phần tiến hành việc tìm hiểu đối tượng xây dựng cho việc xử lý ngoại lệ Đối tượng System.Exception cung cấp số phương thức thuộc tính hữu dụng Thuộc tính Message cung cấp thông tin ngoại lệ, lý ngoại lệ phát sinh Thuộc tính Message thuộc tính đọc, đoạn chương trình phát sinh ngoại lệ thiết lập thuộc tính Message đối mục cho khởi dựng ngoại lệ Thuộc tính HelpLink cung cấp liên kết để trợ giúp cho tập tin liên quan đến ngoại lệ Đây thuộc tính đọc Thuộc tính StackTrace thuộc tính đọc thiết lập CLR Trong ví dụ 13.6 thuộc tính Exception.HelpLink thiết lập truy cập để cung cấp thông tin cho người sử dụng ngoại lệ DivideBy-ZeroException Thuộc tính StackTrace ngoại lệ 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 gọi stack phương thức gọi mà dẫn đến ngoại lệ 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ố xử lý ngoại lệ 376 Xử Lý Ngoại Lệ Ngôn Ngữ Lập Trình C# 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 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(); } return a/b; } } } 377 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# Kết quả: Open file here DivideByZeroExceptión Msg: Attempted to divide by zero HelpLink: http://www.hcmuns.edu.vn Here’s a stack trace: at Programming_CSharp.Test.DoDivide(Double c, Double b) in c:\ exception06.cs: line 56 at Programming_CSharp.Test.TestFunc() in exception06.cs: line 22 Close file here Trong đoạn kết trên, danh sách trace stack hiển thị theo thứ tự ngược lại thứ tự gọi Nó hiển thị lỗi phương thức DoDivde(), phương thức gọi từ phương thức TestFunc() Khi phương thức gọi lồng nhiều cấp, thông tin stack giúp hiểu thứ tự phương thức gọi Trong ví dụ này, việc đơn giản phát sinh DidiveByZeroException, tạo thể hện ngoại lệ: DivideByZeroException e = new DivideByZeroException(); Chúng ta không truyền vào thông điệp chúng ta, nên thông điệp mặc định in ra: DivideByZeroException! Msg: Attemped to divide by zero Ở bổ sung dịng lệnh bên để truyền vào thông điệp tùy chọn sau: new DivideByZeroException(“You tried to divide by zero which is not meaningful”); Trước phát sinh ngoại lệ, thiết lập thuộc tính HelpLink sau: e.HelpLink = “http://www.hcmunc.edu.vn”; Khi ngoại lệ bắt giữ, chương trình in thơng điệp HelpLink hình: catch (System.DivideByZeroException e) { Console.WriteLine(“\nDivideByZeroException! Msg: {0}”, e.Message); Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink); } Việc làm cho phép cung cấp thơng tin hữu ích cho người sử dụng Thêm vào thơng tin stack đưa cách sử dụng thuộc tính StackTrace đối tượng ngoại lệ: Console.WriteLine(“\n Here’s a stack trace: {0}\n”, e.StackTrace); Kết vết stack xuất ra: Here’s a stack trace: at Programming_CSharp.Test.DoDivide(Double c, Double b) 378 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# in c:\ exception06.cs: line 56 at Programming_CSharp.Test.TestFunc() in exception06.cs: line 22 Lưu ý rằng, phần đường dẫn viết tắt, kết bạn khác tí Bảng 13.1 sau mơ tả số lớp ngoại lệ chung khai báo bên namespace System CÁC LỚP NGOẠI LỆ Mô tả Tên ngoại lệ MethodAccessException ArgumentException ArgumentNullException ArithmeticException ArrayTypeMismatchException DivideByZeroException FormatException IndexOutOfRangeException InvalidCastException MulticastNotSupportedException NotFiniteNumberException NotSupportedException NullReferenceException OutOfMemoryException OverflowException StackOverflowException Lỗi truy cập, truy cập đến thành viên hay phương thức không truy cập Lỗi tham số đối mục Đối mục Null, phương thức truyền đối mục null không chấp nhận Lỗi liên quan đến phép toán Kiểu mảng khơng hợp, cố lưu trữ kiểu khơng thích hợp vào mảng Lỗi chia zero Định dạng khơng xác đối mục Chỉ số truy cập mảng không hợp lệ, dùng nhỏ số nhỏ hay lớn số lớn mảng Phép gán không hợp lệ Multicast không hỗ trợ, việc kết hợp hai delegate không Không phải số hữu hạn, số không hợp lệ Phương thức không hỗ trợ, gọi phương thức không tồn bên lớp Tham chiếu null không hợp lệ Out of memory Lỗi tràn phép toán Tràn stack Kiểu khởi tạo sai, khởi dựng tĩnh có lỗi Bảng 13.1 : Các ngoại lệ thường xuất TypeInitializationException Tạo riêng ngoại lệ CLR cung cấp kiểu liệu ngoại lệ bản, ví dụ trước tạo vài kiểu ngoại lệ riêng Thông thường cần thiết phải cung cấp thông tin mở rộng cho khối catch ngoại lệ phát sinh Tuy nhiên, có lúc 379 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# muốn cung cấp nhiều thông tin mở rộng khả đặc biệt cần thiết ngoại lệ mà tạo Chúng ta dễ dàng tạo ngoại lệ riêng, hay gọi ngoại lệ tùy chọn (custom exception), điều bắt buộc với ngoại lệ chúng phải dẫn xuất từ System.ApplicationException Ví dụ 13.7 sau minh họa việc tạo ngoại lệ riêng Ví dụ: Tạo ngoại lệ riêng namespace Programming_CSharp { using System; // tạo ngoại lệ riêng public class MyCustomException : System.ApplicationException { public MyCustomException( string message): base(message) { } } public class Test { public static void Main() { Test t = new Test(); t.TestFunc(); } // chia hai số xử lý ngoại lệ public void TestFunc() { try { Console.WriteLine(“Open file here”); double a = 0; double b = 5; 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); 380 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink); } catch (MyCustomException e) { Console.WriteLine(“\nMyCustomException! Msg: {0}”, e.Message); Console.WriteLine(“\nHelpLink: {0}”, e.HelpLink); } catch { Console.WriteLine(“Unknown excepiton caught”); } finally { Console.WriteLine(“Close file here.”); } } // thực 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) { MyCustomException e = new MyCustomException(“Can’t have zero divisor”); e.HelpLink = “http://www.hcmuns.edu.vn”; throw e; } return a/b; } } } 381 Xử Lý Ngoại Lệ Ngôn Ngữ Lập Trình C# Lớp MyCustomException dẫn xuất từ System.ApplicationException lớp khơng có thực thi hay khai báo ngồi hàm khởi dựng Hàm khởi dựng lấy tham số chuỗi truyền cho lớp sở Trong trường hợp này, lợi ích việc tạo ngoại lệ làm bật điều mà chuơng trình muốn minh họa, tức khơng cho phép số chia zero Sử dụng ngoại lệ ArithmeticException tốt ngoại lệ tạo Nhưng làm nhầm lẫn cho người lập trình khác phép chia với số chia zero lỗi số học Phát sinh lại ngoại lệ Giả sử muốn khối catch thực vài hành động sau phát sinh lại ngoại lệ bên khối catch (trong hàm gọi) Chúng ta phép phát sinh lại ngoại lệ hay phát sinh lại ngoại lệ khác Nếu phát sinh ngoại lệ khác, phải nhúng ngoại lệ ban đầu vào bên ngoại lệ để phương thức gọi hiểu lai lịch nguồn gốc ngoại lệ Thuộc tính InnerException ngoại lệ cho phép truy cập ngoại lệ ban đầu Bởi InnerException ngoại lệ, nên có ngoại lệ bên Do vậy, toàn dây chuyền ngoại lệ đóng tổ (nest) ngoại lệ với ngoại lệ khác Giống lật đật, chứa đến lượt bên lại chứa Ví dụ 13.8: Phát sinh lại ngoại lệ & ngoại lệ inner namespace Programming_CSharp { using System; // tạo ngoại lệ riêng public class MyCustomException : System.Exception { public MyCustomException( string message, Exception inner): base(message, inner) { } } public class Test { public static void Main() { Test t = new Test(); 382 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# t.TestFunc(); } // chia hai số xử lý ngoại lệ public void TestFunc() { try { DangerousFunc1(); } catch (MyCustomException e) { Console.WriteLine(“\n{0}”, e.Message); Console.WriteLine(“Retrieving exception history ”); Exception inner = e.InnerException; while ( inner != null) { Console.WriteLine(“{0}”, inner.Message); inner = inner.InnerException; } } } public void DangerousFunc1() { try { DangerousFunc2(); } catch (System.Exception e) { MyCustomException ex = new MyCustomException(“E3 – Custom Exception Situation”, e); throw ex; } } public void DangerousFunc2() { try { 383 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# DangerousFunc3(); } catch (System.DivideByZeroException e) { Exception ex = new Exception(“E2 - Func2 caught divide by zero”, e); throw ex; } } public void DangerousFunc3() { try { DangerousFunc4(); } catch (System.ArithmeticException) { throw; } catch (System.Exception) { Console.WriteLine(“Exception handled here.”); } } public void DangerousFunc4() { throw new DivideByZeroException(“E1 – DivideByZero Exception”); } } } Kết quả: E3 – Custom Exception Situation! Retrieving exception history E2 - Func2 caught divide by zero E1 – DivideByZeroException Để hiểu rõ ta dùng trình debugger để chạy bước chương trình ta hiểu rõ bước thực thi việc phát sinh ngoại lệ 384 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# Chương trình bắt đầu với việc gọi hàm DangerousFunc1() khối try: try { DangerousFunc1(); } DangerousFunc1() gọi DangerousFunc2(), DangerousFunc2() lại gọi DangerousFunc3(), cuối DangerousFunc3() gọi DangerousFunc4() Tất việc gọi điều nằm khối try Cuối cùng, DangerousFunc4() phát sinh ngoại lệ DivideByzeroException Ngoại lệ bình thường có chứa thơng điệp bên nó, tự dùng thông điệp Để dễ theo dõi đưa vào chuỗi xác nhận kiện diễn Ngoại lệ phát sinh DangerousFunc4() bắt khối catch hàm DangerousFunc3() Khối catch DangerousFunc3() bắt ngoại lệ ArithmeticException ( DivideByZeroException), không thực hành động mà đơn giản phát sinh lại ngoại lệ: catch ( System.ArithmeticException) { throw; } Cú pháp để thực phát sinh lại ngoại lệ mà khơng có bổ sung hay hiệu chỉnh : throw Do ngoại lệ phát sinh cho DangerousFunc2(), khối catch DangerousFunc2() thực vài hành động tiếp tục phát sinh ngoại lệ có kiểu Trong hàm khởi dựng ngoại lệ mới, DangerousFunc2() truyền chuỗi thông điệp (“E2 - Func2 caught divide by zero”) ngoại lệ ban đầu Do ngoại lệ ban đầu (E1) trở thành ngoại lệ bên ngoại lệ (E2) Sau hàm DangerousFunc2() phát sinh ngoại lệ (E2) cho hàm DangerousFunc1() DangerousFunc1() bắt giữ ngoại lệ này, làm số công việc tạo ngoại lệ có kiểu MyCustomException, truyền vào hàm khởi dựng ngoại lệ chuỗi (“E3 – Custom Exception Situation!”) ngoại lệ bắt giữ (E2) Chúng ta nên nhớ ngoại lệ bắt giữ ngoại lệ có chứa ngoại lệ DivideByZeroException (E1) bên Tại thời điểm này, có ngoại lệ kiểu MyCustomException (E3), ngoại lệ chứa bên ngoại lệ kiểu Exception (E2), đến lượt chứa ngoại lệ kiểu DivideByZeroException (E1) bên Sau ngoại lệ phát sinh cho hàm TestFunc; Khi khối catch TestFunc thực in thông điệp ngoại lệ : E3 – Custom Exception Situation! sau ngoại lệ bên lấy thơng qua vịng lặp while: while ( inner != null) { 385 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# Console.WriteLine(“{0}”, inner.Message); inner = inner.InnerException; } Kết chuỗi ngoại lệ phát sinh bắt giữ: Retrieving exception history E2 - Func2 caught divide by zero E1 – DivideByZero Exception Câu hỏi trả lời Câu hỏi 1: Việc sử dụng catch khơng có tham số có nhiều sức mạnh chúng bắt tất ngoại lệ Tại không luôn sử dụng câu lệnh catch khơng có tham số để bắt lỗi? Trả lời 1: Mặc dù sử dụng catch có nhiều sức mạnh, làm nhiều thông tin quan trọng ngoại lệ phát sinh Khi khơng biết xác loại ngoại lệ xảy khó bảo trì khắc phục ngoại lệ sau Về phía người dùng Nếu chương trình gặp ngoại lệ mà khơng có thơng báo rõ ràng cho nguời dùng làm cho họ hoang mang, đổ lỗi cho chương trình khơng tốt lỗi khơng phải ta Ví dụ lỗi hết tài nguyên nhớ người dùng sử dụng nhiều chương trình hoạt động lúc Tóm lại nên sử dụng catch với tham số chi tiết để thực tốt việc quản lý ngoại lệ phát sinh Câu hỏi 2: Có phải tất ngoại lệ đối xử cách bình đẳng? Trả lời 2: Khơng phải, có hai loại ngoại lệ, ngoại lệ hệ thống ngoại lệ chương trình ứng dụng Ngoại lệ chương trình ứng dụng khơng kết thúc chương trình Cịn ngoại lệ hệ thống kết thúc chương trình Nói chung ngoại lệ xuất trước Hiện người ta chia nhiều mức độ ngoại lệ tùy theo mức độ ngoại lệ mà chương trình nhận ứng xử khác Để biết thêm chi tiết đọc thêm tài liệu NET Framework xử lý ngoại lệ Câu hỏi 3: Như câu trả lời bên tơi phải tìm hiểu nhiều ngoại lệ cách thức xử lý ngoại lệ chúng phát sinh? Trả lời 3: Việc xây dựng chương trình ứng dụng phức tạp, chương trình ln tiếm ẩn yếu tố khơng ổn định phát sinh ngoại lệ dẫn đến lỗi không mong muốn Việc thực bắt giữ ngoại lệ cần thiết chương trình, cho phép xây dựng chương trình hồn thiện xử lý thông điệp ngoại lệ tốt Tìm hiểu ngoại lệ đem đến cho nhiều kinh nghiệm việc xây dựng chương trình phức tạp Câu hỏi thêm Câu hỏi 1: Hãy cho biết từ khóa sử dụng để xử lý ngoại lệ? 386 Xử Lý Ngoại Lệ Ngôn Ngữ Lập Trình C# Câu hỏi 2: Phân biệt lỗi ngoại lệ? Câu hỏi 3: Khi thực việc bắt giữ ngoại lệ Nếu có nhiều mức bắt giữ ngoại lệ thực mức Từ chi tiết đến tổng quát, hay từ tổng quát đến chi tiết? Câu hỏi 4: Ý nghĩa từ khóa finally việc xử lý ngoại lệ? Câu hỏi 5: Câu lệnh dùng để phát sinh ngoại lệ? Câu hỏi 6: Loại sau nên xử lý theo ngoại lệ loại nên xử lý mã lệnh thông thường? a Giá trị nhập vào người dùng không nằm mức cho phép b Tập tin không viết mà thực viết c Đối mục truyền vào cho phương thức chứa giá trị không hợp lệ d Đối mục truyền vào cho phương thức chứa kiểu không hợp lệ Câu hỏi 7: Nguyên nhân dẫn đến phát sinh ngoại lệ? Câu hỏi 8: Khi ngoại lệ xuất hiện? a Trong tạo mã nguồn b Trong biên dịch c Trong thực thi chương trình d Khi yêu cầu đựơc đưa ta người dùng cuối Câu hỏi 9: Khi khối lệnh finally thực hiện? Câu hỏi 10: Trong namespace chức lớp liên quan đến việc xử lý ngoại lệ? Hãy cho biết số lớp xử lý ngoại lệ quan trọng namespace này? Bài tập Bài tập 1: Hãy viết đoạn lệnh để thực việc bắt ngoại lệ liên quan đến câu lệnh sau đây: Ketqua = Sothu1 / Sothu2; Bài tập 2: Chương trình sau có vấn đề Hãy xác định vấn đề phát sinh ngoại lệ chạy chương trình Và viết lại chương trình hồn chỉnh gồm lệnh xử lý ngoại lệ: using System; public class Tester { public static void Main() { uint so1=0; int so2, so3; so2 = -10; so3 = 0; // tính giá trị lại 387 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# so1 -= 5; so2 = 5/so3; // xuất kết Console.WriteLine("So 1: {0}, So 2:{1}", so1, so2); } } Bài tập 3: Chương trình sau dẫn đến ngoại lệ hay khơng? Nếu có cho biết ngoại lệ phát sinh Hãy viết lại chương trình hồn chỉnh có xử lý ngoại lệ cách đưa thông điệp ngoại lệ phát sinh using System; using System.IO; public class Tester { public static void Main() { string fname = "test3.txt"; string buffer; StreamReader sReader = File.OpenText(fname); while ( (buffer = sReader.ReadLine()) !=null) { Console.WriteLine(buffer); } } } Bài tập 4: Hãy xem lại ví dụ chương trước, ví dụ phái sinh ngoại lệ thêm đoạn xử lý ngoại lệ cho ví dụ 388 Xử Lý Ngoại Lệ Ngơn Ngữ Lập Trình C# 389 Dùng Visual Studio NET Xây Dựng Ứng Dụng Windows Form ... nhập vào Bài tập 5: Viết chương trình cho phép người dùng nhập vào mảng số nguyên Sau xếp mảng theo thứ tự tăng dần lưu mảng vào tập tin đĩa với dạng nhị phân 364 Các Lớp Cơ Sở NET Ngơn Ngữ Lập Trình. .. StackOverflowException Lỗi truy cập, truy cập đến thành viên hay phương thức không truy cập Lỗi tham số đối mục Đối mục Null, phương thức truy? ??n đối mục null không chấp nhận Lỗi liên quan đến phép toán Kiểu mảng khơng... sử dụng tốt ngơn ngữ khác C# Ngồi lớp BinaryReader cịn có phương thức khác để thực việc đọc kiểu liệu khác Những phương thức đọc sử dụng với cách mà ReadInt32 sử dụng chương trình Bảng 12.4 sau