0

I have a large program I am upgrading in vb.net. I am programming the error handling so that the program can continue to operate even if there is an exception (file doesn't exist etc). But I have got myself confused with all the error handling.

If I have multiple methods that call each other and I put a try catch around the top level method, where is the point that the program will continue to run from after the exception is handled?

For e.g:

public Sub Main()
    Dim a As Integer
    Try
        If (Foo()) Then
            a = Boo()
        End If
    Catch
    End Try
    'Is a 10 here???
End Sub

public Function Foo() As Boolean
    Line1
    Line2
    Line3
    return True
End Function

Public Function Boo() As Int
    Line4
    Line5
    Line6
    return 10
End Function

Would Boo() be called no matter what in this case and a always = 10, for example? - Even if there were exceptions caused on lines 1-6?

JabbaWook
  • 677
  • 1
  • 8
  • 25

1 Answers1

1

No, in your example, if Foo throws an exception, the Catch block will be entered immediately. The Boo function will not be called, and the value of a will not be set.

This would be true even if you Main function did not use any Try/Catch blocks:

Public Sub Main()
    Dim a As Integer
    If (Foo()) Then
        a = Boo()
    End If
End Sub

Boo() will only be called, and a will only be set to its result, if Foo does not throw an exception. If Foo throws, the runtime will search for a suitable exception handler. Not finding one, and this being the top-level method, you'll have an unhandled exception that will then cause the application to be terminated.

This is why exceptions should not be used for flow control, only for actual exceptional conditions.

Put only things that you expect to throw in a Try block, and only have Catch blocks for exceptions that you know how to handle. I would also recommend initializing variables in their declaration whenever possible. Some made-up code as an example of how this should logically work:

Public Sub Main()
    Dim val As Integer = 0    ' 0 is our "default" value

    ' Don't need a Try here, this won't throw an exception.
    ' It will just return an empty string if they canceled.
    Dim fileName As String = AskUserForFileName()

    ' See if they canceled...
    If (fileName <> String.Empty)
        Try
           ' Now we need a Try block, because we're going to do
           ' stuff that might throw an exception.
           Dim file As File = OpenFile(fileName)

           ' Execution won't get here if OpenFile() threw an exception, so
           ' at this point, we know that the file was opened successfully,
           ' so we'll try to read our value from it.
           val = ReadDataFromFile(file)

           ' Again, execution won't get here if ReadDataFromFile() threw
           ' an exception, so we know that the data was read out successfully
           ' and our val variable has been updated.
        Catch ex As FileNotFoundException
           MessageBox.Show("Could not find the specified file.")
        Catch ex As System.IO.IOException
           MessageBox.Show("Could not read from the specified file--check it is valid.")
        End Try
    End If

    ' Our variable val will now contain the value read from the file,
    ' if that whole business was successful. Otherwise, it will
    ' contain the default value of 0 that we set at the top.
End Sub

In real code, you would also make extensive use of Using blocks to handle the automatic cleanup of any object that implements the IDisposable interface in case an exception is thrown. Revising our example above, assuming that the File class I made up implements IDisposable:

Public Sub Main()
    Dim val As Integer = 0    ' 0 is our "default" value

    ' Don't need a Try here, this won't throw an exception.
    ' It will just return an empty string if they canceled.
    Dim fileName As String = AskUserForFileName()

    ' See if they canceled...
    If (fileName <> String.Empty)
        Try
           ' Now we need a Try block, because we're going to do
           ' stuff that might throw an exception.
           Using file As File = OpenFile(fileName)
               ' Execution won't get here if OpenFile() threw an exception, so
               ' at this point, we know that the file was opened successfully,
               ' so we'll try to read our value from it.
               val = ReadDataFromFile(file)

               ' Again, execution won't get here if ReadDataFromFile() threw
               ' an exception, so we know that the data was read out successfully
               ' and our val variable has been updated.
           End Using  ' make sure file always gets closed properly
        Catch ex As FileNotFoundException
           MessageBox.Show("Could not find the specified file.")
        Catch ex As System.IO.IOException
           MessageBox.Show("Could not read from the specified file--check it is valid.")
        End Try
    End If

    ' Our variable val will now contain the value read from the file,
    ' if that whole business was successful. Otherwise, it will
    ' contain the default value of 0 that we set at the top.
End Sub
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Great answer, just one question though? If you don't specify an exception for your `catch` statement, don't all exceptions get handled in that one `catch`? I.e what I had in my example? – JabbaWook Feb 16 '16 at 11:24
  • 1
    @Jabba Yes, they do. And that's really not a good idea. You should only handle exceptions that you know how to handle. Swallowing exceptions that you never expected to see is a bad idea, because it can hide bugs. What if you get an OutOfMemory exception, for example? How are you going to handle that? Trick question: you can't! But if you swallow it, it'll hide a bug. Better to let exceptions you don't know how to deal with bubble up. Indeed, they will crash your program, but you're going to crash anyway because something is messed up and you don't know how to fix it. – Cody Gray - on strike Feb 16 '16 at 11:26
  • More reading: http://stackoverflow.com/questions/4827628/main-method-code-entirely-inside-try-catch-is-it-bad-practice/4827646#4827646 and http://stackoverflow.com/questions/2730078/in-c-how-do-i-know-which-exceptions-to-catch – Cody Gray - on strike Feb 16 '16 at 11:26
  • thanks for the extra links. In this program i'm upgrading(i've inherited it from someone else), most functions that return a value have error handling so that if an error occurs the function returns a default value (most methods have their own `try catch` around the whole thing.) Is this bad practice too? if not what would be the acceptable equivalent? – JabbaWook Feb 16 '16 at 11:43
  • @Jabba Hard to say without seeing the code. In general, it's best not to break something that works. But seeing a bunch of try/catch blocks scattered around throughout the code is unusual, and would look to me like a bit of a code smell. Are there really *that many* exceptions that you're expecting and able to handle? If not, you shouldn't be catching them. File handling is the classical example where you have to deal with exceptions because of race conditions. For GUI code, user input, etc., there's little need for exceptions, just use conditions to check the input. – Cody Gray - on strike Feb 16 '16 at 11:47
  • That said, localizing your try/catch blocks as much as possible *is* a good idea. So if a function is expecting an exception to get thrown and is able to handle it, it should have the try/catch block. Not some function higher up the call stack. The higher you let an exception bubble, the less information you'll have available about what probably caused it and therefore how to fix it. – Cody Gray - on strike Feb 16 '16 at 11:48
  • This code was originally written in `vb6` where i've been tasked with upgrading it too `vb.net`. mostly it involves mail handling and file handling. Most of the functions had `On Error Goto Err_FunctionName` where it would log the error and its origin, and then return a default value. This would be used to handle things like "is this line in a log" "if it isn't then an error will be thrown and the check will return false and the program continues". So i've been torn between ripping these out completely or whether it is actually needed to return a default value. – JabbaWook Feb 16 '16 at 12:00
  • Hmm, I figured that might have been the case. Yes, all of that legacy VB 6 error handling should go. It was terrible in VB 6, but it was the only option. There are much better alternatives in .NET. Not just exceptions, but real object-oriented designs instead of straight procedural code. Of course, VB.NET still supports the On Error Goto syntax for backwards-compatibility reasons, so you don't have to do the migration all at once. – Cody Gray - on strike Feb 16 '16 at 12:12
  • Thank you for your advice, I'm slowing getting an idea of the bigger picture. When you get someone elses code its very hard to understand everything thats going on and the consequence of all the changes you make, but i think i'm just going to rip out most of the error handling and let the program crash and then go through the debug process and get squishing. Think that beats trying to come up with some plausible way to handle things like they did in `vb6`. Thanks again. – JabbaWook Feb 16 '16 at 12:16