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

2022-08-31 07:16:47

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

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

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

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

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

然而,至少对我来说,(1)这感觉像是一个笨拙的解决方案,(2)它导致变量的范围比程序员预期的要大(整个方法的其余部分,而不是仅在尝试捕获最终的上下文中)。

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


答案 1

两件事:

  1. 通常,Java只有两个级别的范围:全局和函数。但是,try/catch是一个例外(没有双关语)。当引发异常并且异常对象获得分配给它的变量时,该对象变量仅在“catch”部分中可用,并在捕获完成后立即销毁。

  2. (更重要的是)。您无法知道 try 块中的哪个位置引发了异常。它可能是在声明变量之前。因此,不可能说出哪些变量可用于 catch/finally 子句。请考虑以下情况,其中范围如您所建议:

    
    try
    {
        throw new ArgumentException("some operation that throws an exception");
        string s = "blah";
    }
    catch (e as ArgumentException)
    {  
        Console.Out.WriteLine(s);
    }
    

这显然是一个问题 - 当您到达异常处理程序时,s 将不会被声明。鉴于 catches 旨在处理特殊情况,并且最终必须执行,因此在编译时安全并声明这是一个问题远比在运行时好得多。


答案 2

你怎么能确定,你到达了你的捕获块的声明部分?如果实例化引发异常怎么办?