How to keep a local variable in scope across a try and catch block?

How to keep a local variable in scope across a try and catch block?

  • Comments 18

The following code won't work, because conn goes out of scope before you enter the catch block.

       try

        {

            Connection conn = new Connection();

            conn.Open();

        }

        catch

        {

            if (conn != null) conn.Close();

        }

 

The fix is simple - just declare conn before entering the try block

 

        Connection conn = null; // Note the assignment to null to avoid error CS0165 - Use of possibly unassigned local variable 'conn'.

        try

        {

            conn = new Connection();

            conn.Open();

        }

        catch

        {

            if (conn != null) conn.Close();

        }

 

Of course, for this particular example, you could wrap the Connection class in one that implements IDisposable (if it does not already), so that you could then use a using statement instead of extending the scope of the local.

 

 

[author: SantoshZ]
Leave a Comment
  • Please add 4 and 8 and type the answer here:
  • Post
  • I disagree with this style. If Open fails, the catch block will be executed and you're calling Close on an unopened connection.

    If you want to undo something, you should place a try { } *AFTER* that action and undo it in a finally:

    Connection conn = new Connection();
    conn.Open();
    try {
    do sth
    } finally {
    conn.Close();
    }
  • The way that scope works in a try-catch block has long been a sore point with me. Why not change C# so that local variables declared within an enclosing try block has a scope that extends to all related catch and finally blocks?

    This would make writing the code much easier and more intuitive, and it would also have the beneficial effect of making the object elligible for collection at the end of the last associated block (catch or finally). When it is declared outside of the scope of the try-catch block its lifetime extends beyond the end of the try-catch block.

  • I always did it this way:

    Connection conn = new Connection();
    try
    {
    conn.Open();
    }
    finally
    {
    conn.Close();
    }

    But were never exactly sure whether this is the way one is supposed to do it ;) Is there a problem with this? I think I don't really understand the code snippet you provided... Why would you use catch when all you want to do is make sure Close is called in all instances?
  • The code you show is an example of bad exception handling.
    It really should be:

    Connection conn = new Connection();
    conn.Open();
    try
    {
    ...
    }
    finally
    {
    conn.Close();
    }

    1) There is no need to call Close() if Open() was not previously executed.
    2) There is no need to call Close() if Open() failed.
    3) You probably want "finally" instead of "catch"
  • I've found in more complex functions its often helpful to maintain scoping by enclosing the try catch and the the variables they share inside of curlys - its not quite syntactic sugar, as it's useful in the following kind of function to make it _very_ clear what the usage scope of variables are

    void myMultiStepFunction()
    {
    object someobject = null;
    DBChannel myChannel = getChannel();
    {
    object carrierObject= null;
    try {
    myChannel.aquire();
    carrierObject= myChannel.getitem();
    someObject = carrierObject.getIt();
    carrierObject.release();
    carrierObject = null;
    }
    finally
    {
    if(myChannel.Aquired)
    myChannel.release();
    if(carrierObject != null)
    carrierObject.release();
    }
    }
    {
    <allocate vars>
    try {
    <perform task>
    }
    finally
    {
    <let go of stuff>
    }
    }
    {
    <allocate vars>
    try {
    <commit operation
    }
    finally
    {
    <let go of stuff>
    }
    }
    <exceptionless cleanup
    }
  • You probably don't want to Close() the connection if Open() threw. Doing so is likely to throw another exception, which could be a problem if you're disposing of other objects in the catch block.

    It would be best to open the connection in the constructor, so that any successfully created Connection can be closed (or disposed).
  • Not to be snide, but this is hardly a "fix". This is basic scoping 101! And it's not even a perfect "fix" at that. Now conn is in scope for the entire procedure after its declaration. Yes, this includes the try block, but also much much more. A using block, assuming IDisposable, would really be the best idea here. I always thought it would be great if variables declared in a try block could also have their scope extended to the associtated catch and finally blocks for just this purpose.
  • CPT 355 students often run into this problem. The following code won't work, because conn goes out of scope before you enter the catch block. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connection conn = new Connection(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.Open(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (conn != null) conn.Close(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp; The fix is simple - just declare conn before entering the try block &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Connection conn = null; // Note the assignment to null to avoid error CS0165 - Use of possibly unassigned local variable 'conn'. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn = new Connection(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.Open(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (conn != null) conn.Close(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp; Of course, for this particular example, you could wrap the Connection class in one that implements IDisposable (if it does not already), so that you could then use a using statement instead of extending the scope of the local. &nbsp; &nbsp; [author: SantoshZ] [Via C# Frequently Asked Questions]...
  • I think when I don’t wont to use using statement, I can use this code:

    Connection conn = new Connection();
    try
    {
    conn.Open();
    }
    finally
    {
    if (conn != null) conn.Close();
    }

    Or maybe something like this:

    Connection conn = new Connection();
    try
    {
    conn.Open();
    }
    finally
    {
    conn.Close();
    }

    Because the object constructor is called before try..finally block, the conn local variable will be assigned. Or will be exception. I think it is unnecessary to call Close() because the object isn’t fully create when is exception in object constructor.

    P.S. Sorry for my bad English.
  • Mark, your code should be like this instead:

    void myMultiStepFunction()
    {
    object someobject;

    DBChannel myChannel = getChannel();
    myChannel.aquire();
    try
    {
    object carrierObject;

    carrierObject = myChannel.getitem();
    try
    {
    someObject = carrierObject.getIt();
    }
    finally
    {
    carrierObject.release();
    }
    }
    finally
    {
    myChannel.release();
    }
    }

    Rule: only protect what you need to protect. What hasn't happened doesn't need to be undone.
  • Connection conn = new Connection();
    try
    {
    conn.Open();
    }
    catch(<appropriate exception>)
    {
    //... log, throw exception.
    }
    finally
    {
    if (conn != null)
    {
    if(conn.state = <ConnectionStatesEnum>.Open)
    {//Close it if it is open - or not closed
    conn.Close();
    }
    conn = null;
    }
    }

    /* I have seen enums like ConnectionStatesEnum for SqlConnection and OLEDBConnection. */

  • (Reply to David Levine's point)

    The reason for the scope is fairly simple, I think - if a variable is declared in a try block, the declaration may not have been reached by the time the catch/finally blocks execute. At best it would be a variable which wasn't definitely assigned, which is fairly useless. Consider:

    int x = 5;

    try
    {
    if (x != 3)
    {
    throw new Exception();
    }
    string s = "hello";
    }
    catch
    {
    // You want to be able to use s here,
    // right? The declaration hasn't been
    // reached yet.
    }

    You *could* change this so that all variables declared in the try block end up with their default value if the first assignment hasn't been reached, but this would be different to all other local variables. I think it's much better to keep it simple, myself.
  • Hi , EveryOne :
    There is a vb and vb.net to c# converter very well here,
    I feel it very good . new version free download, why not try it ?
    http://www.e-iceblue.com
  • The example is unhappy in that we got mixed here two very different things, exception handling properly (try/catch) and returning acquired resources (try/finally).

    For me, a small design 'issue' in C# is that it inherits the try/finally/catch from Java, instead of having two different constructs, try/catch and try/finally, as in Modula-3 or Delphi.

    I almost never use the full try/catch/finally, but one of it's two possible 'specializations'. I often found this pattern helps me avoid variable scope problems like the one mentioned here.

    try {
    Connection c = new Connection();
    c.Open();
    try {
    // read from Connection
    }
    finally {
    c.Close(); // only if c was open'd
    }
    }
    catch (Exception exc) {
    // handle possible problems
    }
    }
Page 1 of 2 (18 items) 12