Home:ALL Converter>Why aren't variables declared in "try" in scope in "catch" or "finally"?

Why aren't variables declared in "try" in scope in "catch" or "finally"?

Ask Time:2008-09-19T01:56:23         Author:Jon Schneider

Json Formatter

In C# and in Java (and possibly other languages as well), variables declared in a "try" block are not in scope in the corresponding "catch" or "finally" blocks. For example, the following code does not compile:

try {
  String s = "test";
  // (more code...)
catch {
  Console.Out.WriteLine(s);  //Java fans: think "System.out.println" here instead

In this code, a compile-time error occurs on the reference to s in the catch block, because s is only in scope in the try block. (In Java, the compile error is "s cannot be resolved"; in C#, it's "The name 's' does not exist in the current context".)

The general solution to this issue seems to be to instead declare variables just before the try block, instead of within the try block:

String s;
try {
  s = "test";
  // (more code...)
catch {
  Console.Out.WriteLine(s);  //Java fans: think "System.out.println" here instead

However, at least to me, (1) this feels like a clunky solution, and (2) it results in the variables having a larger scope than the programmer intended (the entire remainder of the method, instead of only in the context of the try-catch-finally).

My question is, what were/are the rationale(s) behind this language design decision (in Java, in C#, and/or in any other applicable languages)?

Author:Jon Schneider,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/94977/why-arent-variables-declared-in-try-in-scope-in-catch-or-finally
ykaganovich :

The answer, as everyone has pointed out, is pretty much \"that's how blocks are defined\".\n\nThere are some proposals to make the code prettier. See ARM\n\n try (FileReader in = makeReader(), FileWriter out = makeWriter()) {\n // code using in and out\n } catch(IOException e) {\n // ...\n }\n\n\nClosures are supposed to address this as well.\n\nwith(FileReader in : makeReader()) with(FileWriter out : makeWriter()) {\n // code using in and out\n}\n\n\nUPDATE: ARM is implemented in Java 7. http://download.java.net/jdk7/docs/technotes/guides/language/try-with-resources.html",
Guvante :

While in your example it is weird that it does not work, take this similar one:\n\n try\n {\n //Code 1\n String s = \"1|2\";\n //Code 2\n }\n catch\n {\n Console.WriteLine(s.Split('|')[1]);\n }\n\n\nThis would cause the catch to throw a null reference exception if Code 1 broke. Now while the semantics of try/catch are pretty well understood, this would be an annoying corner case, since s is defined with an initial value, so it should in theory never be null, but under shared semantics, it would be.\n\nAgain this could in theory be fixed by only allowing separated definitions (String s; s = \"1|2\";), or some other set of conditions, but it is generally easier to just say no.\n\nAdditionally, it allows the semantics of scope to be defined globally without exception, specifically, locals last as long as the {} they are defined in, in all cases. Minor point, but a point.\n\nFinally, in order to do what you want, you can add a set of brackets around the try catch. Gives you the scope you want, although it does come at the cost of a little readability, but not too much.\n\n{\n String s;\n try\n {\n s = \"test\";\n //More code\n }\n catch\n {\n Console.WriteLine(s);\n }\n}\n",
John Christensen :

Two things:\n\n\nGenerally, Java has just 2 levels of scope: global and function. But, try/catch is an exception (no pun intended). When an exception is thrown and the exception object gets a variable assigned to it, that object variable is only available within the \"catch\" section and is destroyed as soon as the catch completes.\n(and more importantly). You can't know where in the try block the exception was thrown. It may have been before your variable was declared. Therefore it is impossible to say what variables will be available for the catch/finally clause. Consider the following case, where scoping is as you suggested:\n\n\ntry\n{\n throw new ArgumentException(\"some operation that throws an exception\");\n string s = \"blah\";\n}\ncatch (e as ArgumentException)\n{ \n Console.Out.WriteLine(s);\n}\n\n\n\nThis clearly is a problem - when you reach the exception handler, s will not have been declared. Given that catches are meant to handle exceptional circumstances and finallys must execute, being safe and declaring this a problem at compile time is far better than at runtime.",
EndangeredMassa :

You solution is exactly what you should do. You can't be sure that your declaration was even reached in the try block, which would result in another exception in the catch block.\n\nIt simply must work as separate scopes.\n\ntry\n dim i as integer = 10 / 0 ''// Throw an exception\n dim s as string = \"hi\"\ncatch (e)\n console.writeln(s) ''// Would throw another exception, if this was allowed to compile\nend try\n",
tamberg :

The C# Spec (15.2) states \"The scope of a local variable or constant declared in a block ist the block.\"\n\n(in your first example the try block is the block where \"s\" is declared)",
Burkhard :

How could you be sure, that you reached the declaration part in your catch block? What if the instantiation throws the exception?",
jW. :

The variables are block level and restricted to that Try or Catch block. Similar to defining a variable in an if statement. Think of this situation.\n\ntry { \n fileOpen(\"no real file Name\"); \n String s = \"GO TROJANS\"; \n} catch (Exception) { \n print(s); \n}\n\n\nThe String would never be declared, so it can't be depended upon. ",
jpbarto :

My thought would be that because something in the try block triggered the exception its namespace contents cannot be trusted - ie referencing the String 's' in the catch block could cause the throw of yet another exception.",
Ferruccio :

Traditionally, in C-style languages, what happens inside the curly braces stays inside the curly braces. I think that having the lifetime of a variable stretch across scopes like that would be unintuitive to most programmers. You can achieve what you want by enclosing the try/catch/finally blocks inside another level of braces. e.g.\n\n... code ...\n{\n string s = \"test\";\n try\n {\n // more code\n }\n catch(...)\n {\n Console.Out.WriteLine(s);\n }\n}\n\n\nEDIT: I guess every rule does have an exception. The following is valid C++:\n\nint f() { return 0; }\n\nvoid main() \n{\n int y = 0;\n\n if (int x = f())\n {\n cout << x;\n }\n else\n {\n cout << x;\n }\n}\n\n\nThe scope of x is the conditional, the then clause and the else clause.",
Steve Jessop :

In the specific example you've given, initialising s can't throw an exception. So you'd think that maybe its scope could be extended.\n\nBut in general, initialiser expressions can throw exceptions. It wouldn't make sense for a variable whose initialiser threw an exception (or which was declared after another variable where that happened) to be in scope for catch/finally.\n\nAlso, code readability would suffer. The rule in C (and languages which follow it, including C++, Java and C#) is simple: variable scopes follow blocks.\n\nIf you want a variable to be in scope for try/catch/finally but nowhere else, then wrap the whole thing in another set of braces (a bare block) and declare the variable before the try.",
kemiller2002 :

Well if it doesn't throw a compile error, and you could declare it for the rest of the method, then there would be no way to only declare it only within try scope. It's forcing you to be explicit as to where the variable is supposed to exists and doesn't make assumptions. ",
John Rudy :

Everyone else has brought up the basics -- what happens in a block stays in a block. But in the case of .NET, it may be helpful to examine what the compiler thinks is happening. Take, for example, the following try/catch code (note that the StreamReader is declared, correctly, outside the blocks):\n\nstatic void TryCatchFinally()\n{\n StreamReader sr = null;\n try\n {\n sr = new StreamReader(path);\n Console.WriteLine(sr.ReadToEnd());\n }\n catch (Exception ex)\n {\n Console.WriteLine(ex.ToString());\n }\n finally\n {\n if (sr != null)\n {\n sr.Close();\n }\n }\n}\n\n\nThis will compile out to something similar to the following in MSIL:\n\n.method private hidebysig static void TryCatchFinallyDispose() cil managed\n{\n // Code size 53 (0x35) \n .maxstack 2 \n .locals init ([0] class [mscorlib]System.IO.StreamReader sr, \n [1] class [mscorlib]System.Exception ex) \n IL_0000: ldnull \n IL_0001: stloc.0 \n .try \n { \n .try \n { \n IL_0002: ldsfld string UsingTest.Class1::path \n IL_0007: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(string) \n IL_000c: stloc.0 \n IL_000d: ldloc.0 \n IL_000e: callvirt instance string [mscorlib]System.IO.TextReader::ReadToEnd()\n IL_0013: call void [mscorlib]System.Console::WriteLine(string) \n IL_0018: leave.s IL_0028\n } // end .try\n catch [mscorlib]System.Exception \n {\n IL_001a: stloc.1\n IL_001b: ldloc.1 \n IL_001c: callvirt instance string [mscorlib]System.Exception::ToString() \n IL_0021: call void [mscorlib]System.Console::WriteLine(string) \n IL_0026: leave.s IL_0028 \n } // end handler \n IL_0028: leave.s IL_0034 \n } // end .try \n finally \n { \n IL_002a: ldloc.0 \n IL_002b: brfalse.s IL_0033 \n IL_002d: ldloc.0 \n IL_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose() \n IL_0033: endfinally \n } // end handler \n IL_0034: ret \n} // end of method Class1::TryCatchFinallyDispose\n\n\nWhat do we see? MSIL respects the blocks -- they're intrinsically part of the underlying code generated when you compile your C#. The scope isn't just hard-set in the C# spec, it's in the CLR and CLS spec as well. \n\nThe scope protects you, but you do occasionally have to work around it. Over time, you get used to it, and it begins to feel natural. Like everyone else said, what happens in a block stays in that block. You want to share something? You have to go outside the blocks ... ",
Francesca :

Because the try block and the catch block are 2 different blocks. \n\nIn the following code, would you expect s defined in block A be visible in block B? \n\n{ // block A\n string s = \"dude\";\n}\n\n{ // block B\n Console.Out.WriteLine(s); // or printf or whatever\n}\n",
zxcv :

Part of the reason they are not in the same scope is because at any point of the try block, you can have thrown the exception. If they were in the same scope, its a disaster in waiting, because depending on where the exception was thrown, it could be even more ambiguous.\n\nAt least when its declared outside of the try block, you know for sure what the variable at minimum could be when an exception is thrown; The value of the variable before the try block.",
ravenspoint :

In C++ at any rate, the scope of an automatic variable is limited by the curly braces that surround it. Why would anyone expect this to be different by plunking down a try keyword outside the curly braces?",
Wedge :

When you declare a local variable it is placed on the stack (for some types the entire value of the object will be on the stack, for other types only a reference will be on the stack). When there is an exception inside a try block, the local variables within the block are freed, which means the stack is \"unwound\" back to the state it was at at the beginning of the try block. This is by design. It's how the try / catch is able to back out of all of the function calls within the block and puts your system back into a functional state. Without this mechanism you could never be sure of the state of anything when an exception occurs.\n\nHaving your error handling code rely on externally declared variables which have their values changed inside the try block seems like bad design to me. What you are doing is essentially leaking resources intentionally in order to gain information (in this particular case it's not so bad because you are only leaking information, but imagine if it were some other resource? you're just making life harder on yourself in the future). I would suggest breaking up your try blocks into smaller chunks if you require more granularity in error handling.",
Robert Paulson :

If we ignore the scoping-block issue for a moment, the complier would have to work a lot harder in a situation that's not well defined. While this is not impossible, the scoping error also forces you, the author of the code, to realise the implication of the code you write (that the string s may be null in the catch block). If your code was legal, in the case of an OutOfMemory exception, s isn't even guaranteed to be allocated a memory slot: \n\n// won't compile!\ntry\n{\n VeryLargeArray v = new VeryLargeArray(TOO_BIG_CONSTANT); // throws OutOfMemoryException\n string s = \"Help\";\n}\ncatch\n{\n Console.WriteLine(s); // whoops!\n}\n\n\nThe CLR (and therefore compiler) also force you to initialize variables before they are used. In the catch block presented it can't guarantee this. \n\nSo we end up with the compiler having to do a lot of work, which in practice doesn't provide much benefit and would probably confuse people and lead them to ask why try/catch works differently. \n\nIn addition to consistency, by not allowing anything fancy and adhering to the already established scoping semantics used throughout the language, the compiler and CLR are able to provide a greater guarantee of the state of a variable inside a catch block. That it exists and has been initialized.\n\nNote that the language designers have done a good job with other constructs like using and lock where the problem and scope is well defined, which allows you to write clearer code.\n\ne.g. the using keyword with IDisposable objects in:\n\nusing(Writer writer = new Writer())\n{\n writer.Write(\"Hello\");\n}\n\n\nis equivalent to:\n\nWriter writer = new Writer();\ntry\n{ \n writer.Write(\"Hello\");\n}\nfinally\n{\n if( writer != null)\n {\n ((IDisposable)writer).Dispose();\n }\n}\n\n\nIf your try/catch/finally is hard to understand, try refactoring or introducing another layer of indirection with an intermediate class that encapsulates the semantics of what you are trying to accomplish. Without seeing real code, it's hard to be more specific.",
Daren Thomas :

Like ravenspoint pointed out, everyone expects variables to be local to the block they are defined in. try introduces a block and so does catch.\n\nIf you want variables local to both try and catch, try enclosing both in a block:\n\n// here is some code\n{\n string s;\n try\n {\n\n throw new Exception(\":(\")\n }\n catch (Exception e)\n {\n Debug.WriteLine(s);\n }\n}\n",
Jesper Blad Jensen :

When you have a try catch, you should at the most part know that errors that it might throw. Theese Exception classes normaly tell everything you need about the exception. If not, you should make you're own exception classes and pass that information along. That way, you will never need to get the variables from inside the try block, because the Exception is self explainatory. So if you need to do this alot, think about you're design, and try to think if there is some other way, that you can either predict exceptions comming, or use the information comming from the exceptions, and then maybe rethrow your own exception with more information.",
usefulBee :

Instead of a local variable, a public property could be declared; this also should avoid another potential error of an unassigned variable.\npublic string S { get; set; }",
dgvid :

The simple answer is that C and most of the languages that have inherited its syntax are block scoped. That means that if a variable is defined in one block, i.e., inside { }, that is its scope.\n\nThe exception, by the way, is JavaScript, which has a similar syntax, but is function scoped. In JavaScript, a variable declared in a try block is in scope in the catch block, and everywhere else in its containing function.",
Charles Graham :

As has been pointed out by other users, the curly braces define scope in pretty much every C style language that I know of.\n\nIf it's a simple variable, then why do you care how long it will be in scope? It's not that big a deal.\n\nin C#, if it is a complex variable, you will want to implement IDisposable. You can then either use try/catch/finally and call obj.Dispose() in the finally block. Or you can use the using keyword, which will automatically call the Dispose at the end of the code section.",
SaaS Developer :

If the assignment operation fails your catch statement will have a null reference back to the unassigned variable.",
hurst :

According to the section titled \"How to Throw and Catch Exceptions\" in Lesson 2 of MCTS Self-Paced Training Kit (Exam 70-536): Microsoft® .NET Framework 2.0—Application Development Foundation, the reason is that the exception may have occurred before variable declarations in the try block (as others have noted already).\n\nQuote from page 25:\n\n\"Notice that the StreamReader declaration was moved outside the Try block in the preceding example. This is necessary because the Finally block cannot access variables that are declared within the Try block. This makes sense because depending on where an exception occurred, variable declarations within the Try block might not yet have been executed.\"",
adal :

In Python they are visible in the catch/finally blocks if the line declaring them didn't throw.",
Timothy Carter :

@burkhard has the question as to why answered properly, but as a note I wanted to add, while your recommended solution example is good 99.9999+% of time, it is not good practice, it is far safer to either check for null before using something instantiate within the try block, or initialize the variable to something instead of just declaring it before the try block. For example:\n\nstring s = String.Empty;\ntry\n{\n //do work\n}\ncatch\n{\n //safely access s\n Console.WriteLine(s);\n}\n\n\nOr:\n\nstring s;\ntry\n{\n //do work\n}\ncatch\n{\n if (!String.IsNullOrEmpty(s))\n {\n //safely access s\n Console.WriteLine(s);\n }\n}\n\n\nThis should provide scalability in the workaround, so that even when what you're doing in the try block is more complex than assigning a string, you should be able to safely access the data from your catch block.",
Ravi :

What if the exception is thrown in some code which is above the declaration of the variable. Which means, the declaration itself was not happend in this case.\n\ntry {\n\n //doSomeWork // Exception is thrown in this line. \n String s;\n //doRestOfTheWork\n\n} catch (Exception) {\n //Use s;//Problem here\n} finally {\n //Use s;//Problem here\n}\n",