try/catch and scoping

I received an interesting customer question last week, and thought it would be valuable to share the answer here.

Here's the question:

<question>

On the subject of try/catch scope in C#, it would be useful if the scope of variables created in try{} could be extended to it’s corresponding catch{} block. I know that I could put the declaration outside of the try block, but this introduces side effects such as having the object around for longer than intended. Is there a problem with extending the rule of {} scoping in this particular instance? Could it also be extended to the finally{} block?

Thanks,

Gary

</question>

One of our language principles is to adhere to C++ heritage.  This isn’t a particularly strong principle – we use it as a general rule and deviate from it when there is a good reason to do so.  For instance, we thought that the number of bugs and amount of confusion caused by C++’s switch fall-through problem was worth fixing.  I’m not sure we’ve ever discussed having the scopes for try..catch..finally be as you describe.  I can see why this would be valuable, but I doubt that we would think it would be so valuable that we would want to depart from C++ heritage.

 

Changing this scoping rule in retrospect would be difficult since there could be existing code like

try {
object o = new object();
}
catch {
object o = …; // different o
}

that would pose a problem.  Dealing with this would be complex.  Perhaps the try block would act as if it was an enclosing block for the catch, there would be shadowing rules for accessing the try’s o despite the presence of another o in the catch, etc.  Possible, but very very messy – you only get one chance to make a decision like this, and that’s for 1.0.

 

Peter Hallam walked into my office as I was finishing off this mail, and he pointed out another reason why this would be hard.  Or not actually why it would be hard, but why it might not have the desired effect that you might think that it would.  What you’d really like to do is define the variable in the try and use it subsequently in the catch:

try {
...
  object o = new object();
}
catch {
Console.WriteLine(o);
}

Unfortunately, the assignment to ‘o’ or code before it could throw an exception.  Since this is possible, o would not be definitely assigned.  I suggested that it might be possible to get around this by doing a seemingly failsafe assignment first, like

try {
object o = null;
...
object o = new object();
}
catch {
Console.WriteLine(o);
}

but there are circumstances when even the null assignment can throw an exception.  The solution to this is to declare and initialize o outside the try, which defeats the purpose of the suggested feature:

object o = null;
try {
object o = new object();
}
catch {
Console.WriteLine(o);
}

And this seemed like such a simple suggestion at first glance!

 

I asked Anders for comments on my draft blog entry, and here's what he said:

<anders>

The real reason is what Peter Hallam says: You can't assume that variables declared in the try block are definitely assigned on entry to the catch block (e.g. in your example, the "new object()" might throw an out of memory exception). Thus, we'd have to require separate initialization in the catch block, which would defeat the purpose of the suggestion.

Anders

</anders>

 

--Scott