Just curious: Why is the syntax for try catch in C# (Java also?) hard coded for multiple statements? Why doesn\'t the language allow:
int i;
string s = DateT
More or less, this is a play on the dangling else problem.
For example,
if( blah )
if ( more blah )
// do some blah
else
// no blah I suppose
Without curly braces, the else is ambiguous because you don't know if it's associated with the first or second if statement. So you have to fallback on a compiler convention (e.g. in Pascal or C, the compiler assumes the dangling else is associated with the closest if statement) to resolve the ambiguity, or fail the compile entirely if you don't want to allow such ambiguity in the first place.
Similarly,
try
try
// some code that throws!
catch(some blah)
// which try block are we catching???
catch(more blah )
// not so sure...
finally
// totally unclear what try this is associated with.
You could solve it with a convention, where catch blocks are always associated with the closest try, but I find this solution generally allows programmers to write code that is potentially dangerous. For example, in C, this:
if( blah )
if( more blah )
x = blah;
else
x = blahblah;
...is how the compiler would interpret this if/if/else block. However, it's also perfectly legitimate to screw up your indenting and write:
if( blah )
if( more blah )
x = blah;
else
x = blahblah;
...which now makes it appear like the else is associated with the outer if statement, when in fact it is associated with the inner if statement due to C conventions. So I think requiring the braces goes a long way towards resolving ambiguity and preventing a rather sneaky bug (these sorts of issues can be trivial to miss, even during code inspection). Languages like python don't have this issue since indentation and whitespace matter.
Consider the fact that there are really three (or more) code blocks in play here:
try {}
catch (myexcption)
{}
catch (myotherexception)
{}
finally
{}
Keep in mind that these are in the scope of a larger context and the exceptions not caught are potentually caught further up the stack.
Note that this is basically the same thing as a class construct that also has the {} structure.
Say for instance you might have:
try
try
if (iAmnotsane)
beatMe(please);
catch (Exception myexception)
catch (myotherexception)
logerror("howdy")
finally
NOW does that second catch belong to the first or the second try? What about the finally? SO you see the optional/multiple portions make the requirement.
Another way of looking at this…
Given all the maintenance problem that have been created by “if”, “while”, “for” and “foreach” statements without bases, a lot of companies have coding standards that always require bases on statements that act on a “block”.
So they make you write:
if (itIsSo)
{
ASingleLineOfCode();
}
Rather then:
if (itIsSo)
ASingleLineOfCode();
(Note as indenting is not checked by the compiler, it can't be depended on to be right)
A good case could be made for designing a language that always require the bases, but then too many people would have hated C# due to having to always use the bases. However for try/catch there was not an expectation of being able to get away without using the bases, so it was possible to require them without to many people complaining.
Given a choose I would much rather have if/endIf (and while/endWhile) as the block delimiters but the USA got its way on that one. (C got to define what most languages look like rather than Module2, afterall most of what we do is defined by history not logic)
The rational is that it's more maintainable (easier to change, less likely to break, ergo higher quality):
As to why exception handling is different than conditional expressions...
Exception handling will traverse up the stack/scope until it finds a Catch block that will catch the type of exception that was thrown. Forcing scope identifiers makes this check for blocks simplified. Forcing you to scope when dealing with exceptions seems like a good idea, it also is a good indication that this is part of exception handling rather than normal code. Exceptions are exceptions, not something you really want happening normally but know can happen and want to handle when they do happen.
EDIT: There is one more reason which I can think of, is that CATCH is mandatory after a TRY unlike ELSE. Hence there needs to be definite way to define the TRY block.
UPDATE: This question was the subject of my blog on December 4th, 2012. There are a number of insightful comments on the blog that you might also be interested in. Thanks for the great question!
As others have noted, the proposed feature introduces ambiguities that are confusing. I was interested to see if there were any other justifications for the decision to not support the feature, so I checked the language design notes archive.
I see nothing in the language design notes archive that justifies this decision. As far as I know, C# does it that way because that's how other languages with similar syntax do it, and they do it that way because of the ambiguity problem.
I did learn something interesting though. In the initial design of C# there was no try-catch-finally! If you wanted a try with a catch and a finally then you had to write:
try
{
try
{
XYZ();
}
catch(whatever)
{
DEF();
}
}
finally
{
ABC();
}
which, not surprisingly, is exactly how the compiler analyzes try-catch-finally; it just breaks it up into try-catch inside try-finally upon initial analysis and pretends that's what you said in the first place.
If you assume that the designers of C# simply choose to use the same syntax as C++ then the question becomes why are braces necessary with single statements try and catch blocks in C++. The simple answer is that Bjarne Stroustrup thought the syntax was easier to explain.
In The Design and Evolution of C++ Stroustrup writes:
"The try keyword is completely redundant and so are the { } braces except where multiple statements are actually used in a try-block or a handler."
He goes on to give an example where the try keyword and { } are not needed. He then writes:
"However, I found this so difficult to explain that the redundancy was introduced to save support staff from confused users."
Reference: Stroustrup, Bjarne (1994). The Design and Evolution of C++. Addison-Wesley.