Sử dụng Try... Catch..., Finally...
Giới thiệu Xử lý lỗi là phần quan trọng đối với công việc của lập trình viên .NET Framework. Cung cấp một số công cụ rất mạnh để xử lý các lỗi một cách tương đối dễ dàng. Một trong những công cụ này là Try... Catch... Finally... . Tuy nhiên, tôi thấy nhiều bạn viết sai khá nhiều khi sử dụng Try ...
Giới thiệu
Xử lý lỗi là phần quan trọng đối với công việc của lập trình viên .NET Framework. Cung cấp một số công cụ rất mạnh để xử lý các lỗi một cách tương đối dễ dàng. Một trong những công cụ này là Try... Catch... Finally... . Tuy nhiên, tôi thấy nhiều bạn viết sai khá nhiều khi sử dụng Try ... Catch ... Finally ... không chỉ đối với lập trình viên mới bắt đầu, mà còn cả các lập trình viên kinh nghiệm lâu năm. Có rất nhiều cuốn sách và thảo luận về lỗi xử lý trong .NET .Nhưng nhiều người không có cái nhìn tổng quan đối về khối Try... Catch... Finally... Với bài viết này, tôi hy vọng các nhà phát triển phần mềm sẽ có cái nhìn sử dụng hợp lý và chi tiết về khối Try ... Catch ... Finally ...
Các ví dụ
Đối với bài viết này, tôi đã thực hiện ứng dụng ví dụ nhỏ cho mục đích sử dụng khác nhau của Try ... Catch ... Finally .... Ví dụ dễ sử dụng và gỡ lỗi. Ví dụ bao gồm Form duy nhất với một số nút trên đó. Mỗi nút thể hiện việc sử dụng cụ thể Try ... Catch ... Finally ... và sửa lỗi bằng cách sử dụng các điểm ngắt để hiểu đầy đủ. Lưu ý rằng mã chính không thực sự làm. Nó chỉ đơn giản là ném ngoại lệ và xử lý chúng.
UsingTryCatchFinally.jpg
Ví dụ được thực hiện trên Visual Studio 2010 Express Phiên bản sử dụng NET Framework 4.0.. Tuy nhiên, tất cả các mã có thể dễ dàng được sử dụng trong bất kỳ ứng dụng .NET 3.5.
Một số vấn đề cơ bản về ngoại lệ
Luôn luôn có lỗi khi bạn xây dựng ứng dụng phần mềm. Có thể kết nối đến cơ sở dữ liệu thất bại, hoặc mở tập tin mà không tồn tại trên máy tính của người sử dụng hay người dùng không có quyền truy cập. Nếu lỗi xảy ra, .NET Framework tập hợp thông tin sai và ném ngoại lệ. . NET Framework có nhiều kiểu ngoại lệ, chẳng hạn như FileNotFoundException, IndexOutOfRangeException và SqlException. Một số Exception các lớp cung cấp thông tin cụ thể về những gì vừa xảy ra. Ví dụ, SqlException chứa thông tin về mức độ nghiêm trọng của SqlError .
Ngoại lệ được ném ra, tất cả chúng đều kế thừa từ lớp System.Exception. Khi Exception được ném ra bởi NET Framework, nó sẽ tự động tìm kiếm khối Catch đầu tiên (chứa trong khối Try ... Catch ...) trong ngăn xếp. Nếu ngăn xếp không chứa bất kỳ Catch, ngoại lệ không được quản lý xảy ra. Nó sẽ cung cấp cho người dùng cuối rằng ngoại lệ không được quản lý xảy ra và chúng có thể tiếp tục và bỏ qua lỗi hoặc thoát khỏi ứng dụng. Trong trường hợp tốt nhất, ứng dụng của bạn được đóng trong hai cách. Là lập trình viên, bạn thường không muốn người dùng thấy thông báo như vậy. Vì vậy, điều quan trọng là luôn luôn thực hiện xử lý lỗi trong mã của bạn bằng cách sử dụng khối Try ... Catch ....
Thử ví dụ
Khi bạn mở ví dụ dự án, bạn sẽ thấy Form với nhiều nút. Tôi biết các nút lớn màu đỏ trông hấp dẫn, nhưng chúng ta không nhấn nó ngay lập tức. Thay vào đó, đầu tiên xây dựng dự án. Bây giờ hãy vào thư mục gỡ rối của bạn (thường nằm trong thư mục giải pháp của bạn) và chạy file thực thi. Bây giờ bạn có thể nhấn nút lớn màu đỏ. Mà không nhìn rất tốt, đúng không? Bây giờ, nhấn bất kỳ khác của các nút (trừ Close) và nhận thấy sự khác biệt. Quay trở lại các tập tin dự án một lần nữa. Trước khi chạy ứng dụng, tôi muốn bạn nhấn phím tắt Ctrl + Alt + E trong Visual Studio. Điều này sẽ hiển thị hộp thoại ngoại lệ. Hãy chắc chắn rằng cả hai hộp kiểm tra sau 'Common Language Runtime ngoại lệ' được kiểm tra. Điều này sẽ đảm bảo rằng bạn đi vào chế độ gỡ lỗi bất cứ khi nào một ngoại lệ được ném trong mã của bạn. Chúng tôi đề nghị bạn bước đầy đủ thông qua các mã của nút bất kỳ bằng cách sử dụng Breakpoints và F8 để thực hiện theo các con đường ngoại lệ thực hiện khi đối phó với Try ... Catch ... Finally khối.
Try ... Catch ...
Mở Project và nhấn nút đầu tiên trên Form, với Try ... Catch .... nhảy qua mã nguồn.
Private Sub btnSimpleTryCatch_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSimpleTryCatch.Click Try ' Một vài dòng code ở đây ' Lỗi xảy ra! Throw New System.Exception("Một lỗi không mong muốn đã xảy ra") 'Một số mã thêm ở đây ... Chúng sẽ không được thực thi nếu ngoại lệ xảy ra. Catch ex As Exception ' Xử lý các trường hợp ngoại lệ. MessageBox.Show(ex.Message, Me.Text, _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub
Try... Catch... Finally...
Chúng ta hãy chuyển sang nút thứ hai trên Form. Click vào và thấy con trỏ đang chờ đợi! Sau vài giây,...
Private Sub btnSimpleTryCatchFinally_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSimpleTryCatchFinally.Click ' Con trỏ chuyển sang chế độ chờ. Me.Cursor = Cursors.WaitCursor Try ' Một số mã ở đây ... ' Thời gian chờ đợi! Threading.Thread.Sleep(5000) ' Lỗi xảy ra! Throw New System.Exception("Xảy ra lỗi không mong muốn.") ' Một số mã thêm ở đây ... Catch ex As Exception ' Xử lý các trường hợp ngoại lệ. MessageBox.Show(ex.Message, Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) Finally ' Hãy chắc chắn rằng con trỏ được thiết lập trở lại mặc định! Me.Cursor = Cursors.Default End Try End Sub
Nhiều Catches
Trong một số trường hợp, bạn có thể muốn xử lý một số ngoại lệ khác biệt. Ví dụ sau đây cung cấp giải thích của nhiều Catch trong khối Try... Catch... . Ví dụ, đang cố để mở một tập tin trên máy tính của người dùng. Điều gì có thể xảy ra?. Có thể là thư mục của tập tin bạn đang cố mở không tồn tại, có thể tập tin không tồn tại, có thể người dùng không có quyền để mở file hoặc có thể một cái gì đó hoàn toàn bất ngờ xảy ra. Trong trường hợp này, bạn có thể sử dụng nhiều catches, như mã sau đây:
Private Sub btnMultipleCatches_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMultipleCatches.Click Try ' Một số mã ở đây ... ' Hãy chỉ ra rằng chúng ta đang cố mở một tập tin ở đây. ' Lỗi xảy ra! Dim rand As New Random If rand.Next(2) = 1 Then Throw New IO.FileNotFoundException_ ("Tập tin bạn đang cố gắng mở không được tìm thấy. ") Else Throw New System.Exception("Có lỗi xảy ra không rõ.") End If ' Một số mã thêm ở đây ... Catch dirEx As IO.DirectoryNotFoundException ' xử lý DirectoryNotFoundException ở đây. MessageBox.Show(dirEx.Message, "Thư mục không tìm thấy", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch fileEx As IO.FileNotFoundException ' Xử lý FileNotFoundException ở đây. MessageBox.Show(fileEx.Message, "File not found", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch ioEx As IO.IOException ' Xử lý ngoại lệ IO ở đây. MessageBox.Show(ioEx.Message, "IO exception", _ MessageBoxButtons.OK, MessageBoxIcon.Error) Catch ex As Exception ' Xử lý bất kỳ ngoại lệ IO khác ở đây. MessageBox.Show(ex.Message, "Ngoại lệ Không biết", _ MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub
Lưu ý bằng cách nào ngoại lệ được tạo ra sử dụng lớp Ramdom. 50% thời gian, chúng ta sẽ nhận được FileNotFoundException và 50% thời gian, chúng ta sẽ có ngoại lệ cụ thể. Tại thời điểm này, bạn có thể muốn bỏ chọn hộp kiểm mà bạn kiểm tra ở đầu bài viết này bằng cách sử dụng Control + Alt + E và nhấn nút 'Multiple catches' vài lần để xem nó như thế nào
Trường hợp ngoại lệ từ phương thức Deeper
Quan niệm sai lầm phổ biến về Try... Catch ... là khối mã duy nhất. Điều này không đúng trong nhiều trường hợp.
Private Sub btnExceptionsFromDeeperMethods_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnExceptionsFromDeeperMethods.Click Try Dim mm As New ManyMethods ' This method goes into three other methods! mm.GoIntoDeeperMethods() Catch ex As Exception MessageBox.Show(ex.Message & Environment.NewLine & _ "The method which threw me was: " & ex.TargetSite.Name, _ Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub
Public Sub GoIntoDeeperMethods() B() ' Goes into B, which goes into C, which goes into ExceptionMethod, ' which throws an error. MessageBox.Show("You will never see me!", "Some deeper method", _ MessageBoxButtons.OK, MessageBoxIcon.Information) AA() ' Will never be executed. AB() ' Will never be executed. End Sub ' Method B, C, AA and AB show MessageBoxes, ' but only after eventually calling the ExceptionMethod. ' The MessageBoxes will therefore never be prompted. Private Sub ExceptionMethod() ' Some code here... ' Oops! An error occurs! Throw New System.Exception("I am coming from way down here!") ' This returns ' to the first Catch block, in the case in the button Event Handler. ' Some code here... End Sub
Finally trong phương thức Deeper
Private Sub btnFinallyInDeeperMethods_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnFinallyInDeeperMethods.Click Try Dim mm As New ManyMethods ' This method goes into four other methods! mm.GoIntoDeeperMethodsWithFinally() Catch ex As Exception MessageBox.Show(ex.Message & Environment.NewLine & _ "The method which threw me was: " & ex.TargetSite.Name, _ Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub
Public Sub GoIntoDeeperMethodsWithFinally() Try ' This will eventually throw an exception. GoIntoDeeperMethods() Finally ' This code will ALWAYS be executed. MessageBox.Show("You will always see me!", _ "Finally", MessageBoxButtons.OK, MessageBoxIcon.Information) End Try End Sub
Ném nhiều ngoại lệ nhiều lần
Private Sub btnHandlingAnExceptionMultipleTimes_Click_ (ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles btnHandlingAnExceptionMultipleTimes.Click Try Dim mm As New ManyMethods ' This method goes into four other methods! mm.GoIntoDeeperMethodsWithCatch() Catch ex As Exception MessageBox.Show(ex.Message & Environment.NewLine & _ "The method which threw me was: " & _ ex.TargetSite.Name & Environment.NewLine & _ "The method which initially threw me was: _ " & ex.InnerException.TargetSite.Name, _ Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub
Public Sub GoIntoDeeperMethodsWithCatch() Try ' This will eventually throw an exception. GoIntoDeeperMethods() Catch ex As Exception MessageBox.Show("Do some specific error handling here...", _ "Some deeper method", MessageBoxButtons.OK, MessageBoxIcon.Information) ' Rethrow the Exception to the form so the form can handle the user interface. Dim newEx As New System.Exception("I am rethrown!", ex) Throw newEx End Try End Sub
Lồng Try... Catch... Finally...
Private Sub btnNestedTryCatch_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnNestedTryCatch.Click Try ' Some code here... Try ' Some more code here... Dim table As New DataTable Try ' Some more code here... ' Oops! An error occurs! Throw New System.Exception("I come from the third nested try.") ' No Catch here, only a finally. Finally ' This code will always execute. table.Dispose() End Try Catch ex As Exception MessageBox.Show(ex.Message & Environment.NewLine & _ " I was handled in the second Try... Catch... block.", _ Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End Try Catch ex As Exception MessageBox.Show(ex.Message & Environment.NewLine & _ " I was handled in the first Try... Catch... block.", _ Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End Try End Sub
Using... End Using
Private Sub btnUsingEndUsing_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUsingEndUsing.Click Dim table1 As New DataTable Try ' Some code here... Finally table1.Dispose() End Try
' The above code actually does the same as this code! ' The Using... End Using block can be used with any object that ' implements IDisposable and makes sure that your object is disposed ' of, no matter what happens. Using table2 As New DataTable ' Some code here... End Using MessageBox.Show("This button does not actually do something." _ & Environment.NewLine & "Check the source code :)", _ Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Error) End Sub