只是好奇:为什么try catch in C#(Java也?)的语法为多个语句硬编码?为什么语言不允许:
int i;
string s = DateTime.Now.Seconds % 2 == 1 ? "1" : "not 1";
try i = int.Parse(s);
catch i = 0;
这个例子仅用于琐碎的目的。我知道有int.TryParse
。
答案 0 :(得分:66)
考虑到这里存在三个(或更多)代码块的事实:
try {}
catch (myexcption)
{}
catch (myotherexception)
{}
finally
{}
请记住,这些都在更大的上下文范围内,未捕获的异常会在堆栈中进一步发挥作用。
请注意,这与具有{}结构的类构造基本相同。
比如说你可能有:
try
try
if (iAmnotsane)
beatMe(please);
catch (Exception myexception)
catch (myotherexception)
logerror("howdy")
finally
NOW第二次尝试是属于第一次还是第二次尝试?最后怎么样?所以你看到可选/多个部分符合要求。
答案 1 :(得分:50)
更新:此问题是my blog on December 4th, 2012的主题。在博客上有许多你可能也感兴趣的富有洞察力的评论。感谢您提出的好问题!
正如其他人所指出的那样,所提出的特征引入了令人困惑的模糊性。我有兴趣看看是否有任何其他理由决定不支持该功能,所以我检查了语言设计说明档案。
我在语言设计备注档案中看不到任何证明这一决定的理由。据我所知,C#就是这样做的,因为这是其他具有类似语法的语言的做法,并且由于模糊性问题,他们就是这样做的。
我确实学到了一些有趣的东西。在C#的初始设计中,没有try-catch-finally!如果你想尝试使用catch和a finally,那么你必须写:
try
{
try
{
XYZ();
}
catch(whatever)
{
DEF();
}
}
finally
{
ABC();
}
毫不奇怪,这正是编译器分析try-catch-finally的原因;它只是在最初的分析中将它分解为try-catch内部的try-catch,并假装这首先是你所说的。
答案 2 :(得分:8)
或多或少,这是dangling else problem上的游戏。
例如,
if( blah )
if ( more blah )
// do some blah
else
// no blah I suppose
如果没有花括号,则else是不明确的,因为您不知道它是否与第一个或第二个if语句相关联。因此,您必须回避编译器约定(例如,在Pascal或C中,编译器假定悬挂的else与最接近的if语句相关联)以解决歧义,或者如果您不希望允许这种歧义,则完全无法编译首先。
类似地,
try
try
// some code that throws!
catch(some blah)
// which try block are we catching???
catch(more blah )
// not so sure...
finally
// totally unclear what try this is associated with.
您可以使用约定来解决它,其中catch块始终与最接近的try相关联,但我发现此解决方案通常允许程序员编写具有潜在危险的代码。例如,在C中,这个:
if( blah )
if( more blah )
x = blah;
else
x = blahblah;
...编译器如何解释if / if / else块。然而,搞砸你的缩进并写下来也是完全合法的:
if( blah )
if( more blah )
x = blah;
else
x = blahblah;
...现在看起来像else与外部if语句相关联,实际上由于C约定它与内部if语句相关联。因此,我认为要求括号在解决模糊性和防止相当偷偷摸摸的错误方面有很长的路要走(即使在代码检查期间,这些问题也很容易被忽略)。像python这样的语言没有这个问题,因为缩进和空格很重要。
答案 3 :(得分:7)
如果你假设C#的设计者只是选择使用与C ++相同的语法,那么问题就变成了为什么单个语句需要使用大括号来尝试和捕获C ++中的块。简单的答案是Bjarne Stroustrup认为语法更容易解释。
在 The Design and Evolution of C++ Stroustrup写道:
“try关键字是完全冗余的,除了在try-block或处理程序中实际使用多个语句的情况下,{}括号也是如此。”
他继续举例说明不需要try关键字和{}。然后他写道:
“但是,我发现这很难解释,引入冗余是为了避免困惑用户的支持人员。”
参考: Stroustrup,Bjarne(1994)。 C ++的设计与演变。 Addison-Wesley出版社。
答案 4 :(得分:1)
我能想到的第一个想法是花括号创建一个具有自己变量范围的块。
请查看以下代码
try
{
int foo = 2;
}
catch (Exception)
{
Console.WriteLine(foo); // The name 'foo' does not exist in the current context
}
由于变量作用域,在catch块中无法访问 foo
。我认为这样可以更容易地判断变量是否在使用前已经初始化。
与此代码比较
int foo;
try
{
foo = 2;
}
catch (Exception)
{
Console.WriteLine(foo); // Use of unassigned local variable 'foo'
}
这里你不能保证foo被初始化。
答案 5 :(得分:1)
try // 1
try // 2
something();
catch { // A
}
catch { // B
}
catch { // C
}
B捕获尝试1还是2?
我不认为你可以毫不含糊地解决这个问题,因为代码片段可能意味着:
try // 1
{
try // 2
something();
catch { // A
}
}
catch { // B
}
catch { // C
}
try // 1
{
try // 2
something();
catch { // A
}
catch { // B
}
}
catch { // C
}
答案 6 :(得分:0)
理性是它更易于维护(更容易更改,更不容易破坏,更高质量):
至于为什么异常处理与条件表达式不同......
异常处理将遍历堆栈/作用域,直到找到一个Catch块,它将捕获抛出的异常类型。强制范围标识符可以简化对块的检查。在处理异常时强制您使用范围似乎是个好主意,它也是一个很好的迹象,表明这是异常处理的一部分而不是普通代码。例外情况是例外情况,不是你真正想要正常发生的事情,但知道可能发生并希望在事情发生时进行处理。
编辑:还有一个我能想到的原因,就是在与ELSE不同的TRY之后,CATCH是强制性的。因此,需要有明确的方法来定义TRY块。答案 7 :(得分:0)
可能会阻止过度使用。 try-catch块很大而且很难看,当你使用它时你会注意到它。这反映了catch对应用程序性能的影响 - 与简单的布尔测试相比,捕获异常非常慢。
一般来说,你应该避免错误,不要处理它们。在你给出的例子中,一个更有效的方法是使用
if(!int.TryParse(s, out i))
i=0;
答案 8 :(得分:0)
另一种看待这个的方式......
鉴于所有由“if”,“while”,“for”和“foreach”语句创建的维护问题没有基础,很多公司都有编码标准,总是需要基于“...”的语句。块”。
所以他们让你写:
if (itIsSo)
{
ASingleLineOfCode();
}
而不是:
if (itIsSo)
ASingleLineOfCode();
(注意,编译器不会检查缩进,不能依赖于缩进)
设计一种总是需要基础的语言可以成为一个很好的案例,但是由于必须始终使用基础,太多人会厌恶C#。然而,对于try / catch而言,并没有期望能够在不使用基础的情况下逃脱,因此可以在没有许多人抱怨的情况下要求它们。
给定一个选择我更愿意将if / endIf(和/ endWhile)作为块分隔符,但是美国在那个方面取得了成功。 (C必须定义大多数语言的样子而不是Module2,毕竟我们所做的大部分是由历史而不是逻辑定义的)
答案 9 :(得分:-3)
最简单的(我认为)答案是C / C ++ / C#中的每个代码块都需要大括号。
编辑#1
直接来自MSDN的回复投票:
try-catch语句包含一个try 块,后跟一个或多个catch子句,它们指定不同异常的处理程序。
根据定义,它是一个块,所以它需要花括号。这就是为什么我们不能在没有{ }
的情况下使用它。