当前位置:  开发笔记 > 编程语言 > 正文

为什么在"catch"或"finally"范围内的"try"中没有声明变量?

如何解决《为什么在"catch"或"finally"范围内的"try"中没有声明变量?》经验,为你挑选了7个好方法。

在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#中,它是"当前上下文中不存在名称".)

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

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

然而,至少在我看来,(1)这感觉就像一个笨重的解决方案,并且(2)它导致变量具有比程序员预期更大的范围(方法的整个剩余部分,而不仅仅是在的try-catch-最后).

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



1> John Christe..:

两件事情:

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

    (更重要的是).您无法知道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将不会被声明.鉴于catchs旨在处理特殊情况并最终必须执行,安全并在编译时声明这个问题远比运行时好.


在编译语言(如Java或C#)中,只要变量在范围内,它的声明位置无关紧要 - 它可以在该范围内的任何其他位置使用.
嗨,你的解释是完全正确的.但是,为什么try/catch定义为它?为什么不用嵌套的catch/finally块定义try catch,即try {... catch {...} finally {...}}.(编译器必须将变量声明移动到块的开头,以使它们在catch中可用.

2> Burkhard..:

你怎么能确定你到达了catch区的声明部分?如果实例化抛出异常怎么办?


咦?变量声明不会抛出异常.
同意,这是可能抛出异常的实例化.

3> Ferruccio..:

传统上,在C风格的语言中,花括号内的内容会留在花括号内.我认为在这样的范围内拥有变量的生命周期对大多数程序员来说都是不直观的.你可以通过将try/catch/finally块包含在另一个括号内来实现你想要的.例如

... code ...
{
    string s = "test";
    try
    {
        // more code
    }
    catch(...)
    {
        Console.Out.WriteLine(s);
    }
}

编辑:我想每一个规则有例外.以下是有效的C++:

int f() { return 0; }

void main() 
{
    int y = 0;

    if (int x = f())
    {
        cout << x;
    }
    else
    {
        cout << x;
    }
}

x的范围是conditional,then子句和else子句.



4> John Rudy..:

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

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中的以下内容:

.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规范中.

范围保护您,但您偶尔必须解决它.随着时间的推移,你会习惯它,并开始感到自然.像其他人说的那样,块中发生的事情会停留在该块中.你想分享一些东西吗?你必须走出街区......



5> ravenspoint..:

无论如何,在C++中,自动变量的范围受到它周围的花括号的限制.为什么有人会期望通过在花括号外面删除try关键字来改变它?



6> Daren Thomas..:

就像ravenspoint指出的那样,每个人都希望变量在定义它们的块中是局部的。try引入一个块,所以也是如此catch

如果希望变量对于try和都是局部的catch,请尝试将它们都包含在一个块中:

// here is some code
{
    string s;
    try
    {

        throw new Exception(":(")
    }
    catch (Exception e)
    {
        Debug.WriteLine(s);
    }
}



7> dgvid..:

简单的答案是C和大多数继承了其语法的语言都是块作用域.这意味着如果变量在一个块中定义,即在{}内,那就是它的范围.

顺便说一句,例外是JavaScript,它具有类似的语法,但是功能范围.在JavaScript中,try块中声明的变量在catch块的范围内,以及其包含函数中的其他位置.

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有