首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么在"try“中声明的变量不在"catch”或"finally“的作用域中?

为什么在"try“中声明的变量不在"catch”或"finally“的作用域中?
EN

Stack Overflow用户
提问于 2008-09-18 17:56:24
回答 23查看 88.5K关注 0票数 157

在C#和Java语言中(可能还有其他语言),在"try“块中声明的变量不在相应的"catch”或"finally“块的作用域中。例如,以下代码无法编译:

代码语言:javascript
复制
try {
  String s = "test";
  // (more code...)
}
catch {
  Console.Out.WriteLine(s);  //Java fans: think "System.out.println" here instead
}

在此代码中,catch块中对s的引用发生编译时错误,因为s仅在try块的作用域中。(在Java语言中,编译错误是“无法解析”;在C#中,错误是“名称"s”在当前上下文中不存在“)。

这个问题的一般解决方案似乎是在try块之前声明变量,而不是在try块内:

代码语言:javascript
复制
String s;
try {
  s = "test";
  // (more code...)
}
catch {
  Console.Out.WriteLine(s);  //Java fans: think "System.out.println" here instead
}

然而,至少对我来说,(1)这感觉像是一个笨拙的解决方案,(2)它导致变量的作用域比程序员预期的更大(整个方法的其余部分,而不仅仅是try-catch-finally的上下文)。

我的问题是,这个语言设计决策背后的理由是什么(在Java、C#和/或任何其他适用的语言中)?

EN

回答 23

Stack Overflow用户

发布于 2008-09-18 17:58:40

您如何确定已到达catch块中的声明部分?如果实例化抛出异常怎么办?

票数 55
EN

Stack Overflow用户

发布于 2008-09-18 21:48:08

其他每个人都提出了基础知识--块中发生的事情将保留在块中。但在.NET的情况下,检查编译器认为正在发生的事情可能会有所帮助。例如,以下面的try/catch代码为例(请注意,StreamReader是在块之外正确声明的):

代码语言:javascript
复制
static void TryCatchFinally()
{
    StreamReader sr = null;
    try
    {
        sr = new StreamReader(path);
        Console.WriteLine(sr.ReadToEnd());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
    finally
    {
        if (sr != null)
        {
            sr.Close();
        }
    }
}

这将在MSIL中编译为类似于以下内容:

代码语言:javascript
复制
.method private hidebysig static void  TryCatchFinallyDispose() cil managed
{
  // Code size       53 (0x35)    
  .maxstack  2    
  .locals init ([0] class [mscorlib]System.IO.StreamReader sr,    
           [1] class [mscorlib]System.Exception ex)    
  IL_0000:  ldnull    
  IL_0001:  stloc.0    
  .try    
  {    
    .try    
    {    
      IL_0002:  ldsfld     string UsingTest.Class1::path    
      IL_0007:  newobj     instance void [mscorlib]System.IO.StreamReader::.ctor(string)    
      IL_000c:  stloc.0    
      IL_000d:  ldloc.0    
      IL_000e:  callvirt   instance string [mscorlib]System.IO.TextReader::ReadToEnd()
      IL_0013:  call       void [mscorlib]System.Console::WriteLine(string)    
      IL_0018:  leave.s    IL_0028
    }  // end .try
    catch [mscorlib]System.Exception 
    {
      IL_001a:  stloc.1
      IL_001b:  ldloc.1    
      IL_001c:  callvirt   instance string [mscorlib]System.Exception::ToString()    
      IL_0021:  call       void [mscorlib]System.Console::WriteLine(string)    
      IL_0026:  leave.s    IL_0028    
    }  // end handler    
    IL_0028:  leave.s    IL_0034    
  }  // end .try    
  finally    
  {    
    IL_002a:  ldloc.0    
    IL_002b:  brfalse.s  IL_0033    
    IL_002d:  ldloc.0    
    IL_002e:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()    
    IL_0033:  endfinally    
  }  // end handler    
  IL_0034:  ret    
} // end of method Class1::TryCatchFinallyDispose

我们看到了什么?MSIL尊重这些块--它们本质上是编译C#时生成的底层代码的一部分。作用域不仅在C#规范中是硬设置的,在CLR和CLS规范中也是如此。

作用域可以保护您,但您有时确实需要绕过它。随着时间的推移,你习惯了它,它开始感觉很自然。就像其他人所说的,在一个区块中发生的事情只会在那个区块中发生。你想分享些什么吗?你得走出街区。

票数 11
EN

Stack Overflow用户

发布于 2008-09-18 17:59:19

无论如何,在C++中,自动变量的作用域受到它周围的大括号的限制。为什么有人会期望在花括号外插入try关键字会有所不同呢?

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/94977

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档