Why aren’t Try variables in scope in the Catch or Finally?

Bill McCarthy recently asked me a question that’s come up a number of times both internally and externally: why can’t a Catch or Finally block access local variables declared in a Try block? In other words, why doesn’t the following work?

Try
    Dim i As Integer = 5
Finally
    Console.WriteLine(i)
End Try

There are four answers to the question: the practical answer, the simple answer, the complex answer and the real answer.

THE PRACTICAL ANSWER: The question is moot because changing it now would potentially break code by changing the binding path. For example:

Class Foo
    Private i As Integer
    Sub Bar()
        Try
            Dim i As Integer
            Throw New NullReferenceException()
        Catch
            i = 5
        End Try
    End Sub
End Class

THE SIMPLE ANSWER: Catch and Finally blocks are not children of the Try block, they’re siblings. You can see this by the indentation style and by convention. And the normal scoping rules are that sibling blocks cannot see each other’s locals.

THE COMPLEX ANSWER: You can still say “Yes, but you could still have made an exception in this case, right?” True. However, requiring Catch/Finally variables to be placed outside of the Try block generally results in better code by emphasizing the initial state of the variable (since an exception can occur almost at any time, so the initial state is the only thing you can assume). For example, the following code has a bug — if the File constructor throws an exception, then the Close call is going to throw one too.

Try
    Dim F As File = New File("foo.txt")
    F.Read(...)
Finally
    F.Close()
End Try

THE REAL ANSWER: You may find both of these arguments unpersuasive, which is fine — a quick search on Google quickly found similar debates around Java and I’m sure other languages have them too. The real answer is that it’s a judgment call and certainly one that we’ll probably be arguing about from here until eternity…

6 thoughts on “Why aren’t Try variables in scope in the Catch or Finally?

  1. stuandgravy

    A simpler version of your complex answer… Although the compiler might reorder statements, the following code doesn’t make logical sense:

    <PRE>Try

    Throw New ApplicationException(msg)



    Dim s As String = "This statement is never reached"



    Finally

    Trace.Write(s)

    End Try</PRE>

    Reply
  2. Mike Schinkel

    I guess you could always extend the language like this:

    Try With i As Integer = 5

    ‘ Do something that hurts…

    Finally

    Console.WriteLine(i)

    End Try

    But you’d probably call that syntactic sugar, eh? (But I *like* sweets! 🙂

    Reply
  3. Pingback: Anonymous

  4. Pingback: Anonymous

  5. Ruaridh Hutchison

    I personally find it counter-intuitive that variables declared in the try block are not in scope in the catch / finally blocks. The catch / finally blocks are part-and-parcel of the try construct. Can a catch or finally block exist in its own right? Of course not.

    Interestingly, the using construct behaves intuitively. A variable declared in a using statement preceding the brackets will be in scope only within the enclosing brackets as you would expect. Knowing what we know about the try construct and scope, you might wonder how this is achieved! It all becomes clear if you look at the C# language specification.

    A using construct such as this:

    using (Resource r = new Resource())

    {

    r.DoSomething();

    }

    gets expanded into this:

    {

    Resource r = new Resource();

    try

    {

    r.DoSomething();

    }

    finally

    {

    ((IDisposable)r).Dispose();

    }

    }

    Of particular interest is the outer set of brackets! I rest my case.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *