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

Published 12 August 04 06:33 PM

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]

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Matthias Ernst said on August 13, 2004 12:24 AM:
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();
}
# David Levine said on August 13, 2004 2:27 AM:
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.

# David said on August 13, 2004 2:57 AM:
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?
# Fabrice said on August 13, 2004 3:19 AM:
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"
# Mark Mullin said on August 13, 2004 5:56 AM:
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
}
# David A. Mellis said on August 13, 2004 7:30 AM:
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).
# Eisbaer said on August 13, 2004 12:04 PM:
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.
# Mikel Berger said on August 13, 2004 6:50 PM:
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]...
# Marek_J said on August 14, 2004 6:16 PM:
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.
# Fabrice said on August 17, 2004 11:14 AM:
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.
# Trilok said on August 18, 2004 5:29 AM:
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. */

# Jon Skeet said on August 20, 2004 3:29 AM:
(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.
# Jack Lee said on August 23, 2004 3:46 AM:
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
# Octavio Hernandez said on August 25, 2004 6:06 AM:
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
}
}
# FAQ C# said on May 2, 2005 8:27 PM:
# FAQ C# (par Yannick Lejeune) said on August 23, 2005 5:14 AM:
# FAQ C# (par Yannick Lejeune) said on August 23, 2005 5:21 AM:

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

This Blog

Syndication

Page view tracker