C# 为什么不在“catch”或“finally”范围内的“try”中声明变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/94977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 11:38:58  来源:igfitidea点击:

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

提问by Jon Schneider

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:

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

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".)

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

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

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

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).

但是,至少对我来说,(1) 这感觉像是一个笨拙的解决方案,并且 (2) 它导致变量具有比程序员预期的更大的范围(方法的整个其余部分,而不仅仅是在尝试-捕获-最后)。

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)?

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

采纳答案by John Christensen

Two things:

两件事情:

  1. Generally, 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.

  2. (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:

    
    try
    {
        throw new ArgumentException("some operation that throws an exception");
        string s = "blah";
    }
    catch (e as ArgumentException)
    {  
        Console.Out.WriteLine(s);
    }
    
  1. 通常,Java 只有 2 个级别的作用域:全局和函数。但是,try/catch 是一个例外(没有双关语)。当抛出异常并且异常对象获得分配给它的变量时,该对象变量仅在“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);
    }
    

This 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 mustexecute, being safe and declaring this a problem at compile time is far better than at runtime.

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

回答by Burkhard

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

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

回答by 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.

我的想法是,因为 try 块中的某些内容触发了异常,其命名空间内容不可信任 - 即在 catch 块中引用 String 's' 可能会导致抛出另一个异常。

回答by 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.

好吧,如果它没有抛出编译错误,并且您可以为方法的其余部分声明它,那么将无法仅在 try 范围内声明它。它迫使您明确变量应该存在的位置并且不进行假设。

回答by 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?

无论如何,在 C++ 中,自动变量的范围受围绕它的花括号限制。为什么有人希望通过在大括号外插入一个 try 关键字来实现这一点?

回答by 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.

您的解决方案正是您应该做的。您无法确定甚至在 try 块中达到了您的声明,这将导致 catch 块中出现另一个异常。

It simply must work as separate scopes.

它必须作为单独的作用域工作。

try
    dim i as integer = 10 / 0 ''// Throw an exception
    dim s as string = "hi"
catch (e)
    console.writeln(s) ''// Would throw another exception, if this was allowed to compile
end try

回答by SaaS Developer

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

如果赋值操作失败,您的 catch 语句将有一个空引用返回到未分配的变量。

回答by 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.

变量是块级的,并且仅限于 Try 或 Catch 块。类似于在 if 语句中定义变量。想想这种情况。

try {    
    fileOpen("no real file Name");    
    String s = "GO TROJANS"; 
} catch (Exception) {   
    print(s); 
}

The String would never be declared, so it can't be depended upon.

String 永远不会被声明,所以它不能被依赖。

回答by 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.

在您给出的具体示例中,初始化 s 不能引发异常。所以你会认为它的范围可能会扩大。

But 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.

但一般来说,初始化表达式可以抛出异常。对于其初始化程序抛出异常(或在发生异常的另一个变量之后声明)的变量在 catch/finally 的范围内是没有意义的。

Also, code readability would suffer. The rule in C (and languages which follow it, including C++, Java and C#) is simple: variable scopes follow blocks.

此外,代码可读性也会受到影响。C(以及遵循它的语言,包括 C++、Java 和 C#)中的规则很简单:变量作用域遵循块。

If 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.

如果你想让一个变量在 try/catch/finally 的范围内但没有其他地方,那么把整个事情包装在另一组大括号(一个裸块)中,并在 try 之前声明变量。

回答by 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.

传统上,在 C 风格的语言中,花括号内发生的事情会留在花括号内。我认为对于大多数程序员来说,让变量的生命周期跨越这样的范围是不直观的。您可以通过将 try/catch/finally 块包含在另一层大括号内来实现您想要的。例如

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

EDIT: I guess every rule doeshave an exception. The following is valid C++:

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

int f() { return 0; }

void main() 
{
    int y = 0;

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

The scope of x is the conditional, the then clause and the else clause.

x 的作用域是条件句、then 子句和 else 子句。