49


7

Try-Catchに中括弧が必要な理由

好奇心が強い:http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx[C#でcatchを試す](Javaも?)の構文が複数のステートメントにハードコードされているのはなぜですか? 言語が許可しない理由:

int i;
string s = DateTime.Now.Seconds % 2 == 1 ? "1" : "not 1";

try i = int.Parse(s);
catch i = 0;

この例は、些細な目的のためだけのものです。 http://msdn.microsoft.com/en-us/library/system.int32.tryparse.aspx [int.TryParse]があることは知っています。

10 Answer


66


ここでは、実際に3つ(またはそれ以上)のコードブロックが動作しているという事実を考慮してください。

try {}
catch (myexcption)
{}
catch (myotherexception)
{}
finally
{}

これらはより大きなコンテキストの範囲内にあり、キャッチされない例外はスタックのさらに上位に強力にキャッチされることに注意してください。

これは、基本的に\ {}構造を持つクラス構成体と基本的に同じものであることに注意してください。

たとえば、あなたが持っているかもしれないと言う:

try
try
if (iAmnotsane)
beatMe(please);
catch (Exception myexception)
catch (myotherexception)
logerror("howdy")
finally

今、その2番目のキャッチは1回目または2回目の試行に属しますか? 最終的にはどうですか? オプション/複数の部分が要件を満たしていることがわかります。


50


*更新:*この質問はhttp://ericlippert.com/2012/12/04/why-are-braces-required/[2012年12月4日の私のブログ]の主題でした。 ブログには、あなたが興味を持っているかもしれない洞察に満ちたコメントがたくさんあります。 素晴らしい質問をありがとう!

'' '' '

他の人が指摘したように、提案された機能は混乱を招く曖昧さをもたらします。 この機能をサポートしないという決定に正当な理由があるかどうかを確認したいので、言語設計ノートのアーカイブを確認しました。

言語設計ノートのアーカイブには、この決定を正当化するものは何もありません。 私の知る限り、C#は同じような構文を持つ他の言語がそれを行う方法であるため、C#がその方法で実行します。

でも面白いことを学びました。 C#の初期設計では、try-catch-finallyはありませんでした! あなたがキャッチと最後に試してみたいなら、あなたは書く必要がありました:

try
{
  try
  {
      XYZ();
  }
  catch(whatever)
  {
     DEF();
  }
}
finally
{
  ABC();
}

当然のことながら、コンパイラーがtry-catch-finallyを正確に分析する方法です。最初の分析でtry-finally内でtry-catchに分割し、最初に言ったことを装います。


8


多かれ少なかれ、これはhttp://en.wikipedia.org/wiki/Dangling_else[dangling else problem]のプレイです。

例えば、

if( blah )
    if ( more blah )
        // do some blah
else
    // no blah I suppose

中括弧がない場合、elseは、最初のifステートメントと2番目の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;

…​is how the compiler would interpret this if/if/else block. However, インデントを台無しにして書くことも完全に合法です:

if( blah )
    if( more blah )
        x = blah;
else
    x = blahblah;

…​which now makes it appear like the else is associated with the outer ifステートメント。実際には、Cの規約により、内部のifステートメントに関連付けられています。 そのため、ブレースを要求することは、あいまいさを解決し、かなり卑劣なバグを防止するのに大いに役立つと思います(これらの種類の問題は、コード検査中であっても見落としがちです)。 インデントと空白が問題になるため、Pythonのような言語にはこの問題はありません。


7


C#の設計者が単にC と同じ構文を使用することを選択すると仮定すると、単一ステートメントでブレースが必要な理由がC でブロックをキャッチしようとする理由になります。 簡単な答えは、http://stroustrup.com/ [Bjarne Stroustrup]は構文の説明が簡単だと思ったということです。

_https://rads.stackoverflow.com/amzn/click/com/0201543303 [C ++の設計と進化] _ St​​roustrupは次のように書いています。

_ "tryキーワードは完全に冗長であるため、複数のステートメントがtryブロックまたはハンドラーで実際に使用される場合を除き、\ {}中括弧も同様です。" _

さらに、tryキーワードと\ {}が不要な例を示します。 次に彼はこう書いている:

「しかし、混乱したユーザーからサポートスタッフを救うために冗長性が導入されたことを説明するのは非常に難しいと感じました。」

参照:Stroustrup、Bjarne(1994)。 C ++の設計と進化。 アディソン - ウェズリー


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が初期化されることを保証できません。


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
}


0


合理的なのは、保守性が高いことです(変更しやすく、壊れにくく、品質が向上します)。

  1. より明確で、そして

  2. ブロックに行を追加する必要がある場合は変更が簡単です バグを導入しません。

例外処理が条件式と異なる理由について…​

  • If / Elseは、2つ(またはそれ以上)のいずれかを使用する式を条件とします If / Else if / Else)コード内のパス

  • Try / Catchは例外処理の一部であり、条件付きではありません 式です。 Try / Catch / Finallyは、Tryブロックのスコープ内で例外がスローされた場合にのみ動作します。

例外処理は、スローされた例外のタイプをキャッチするCatchブロックが見つかるまでスタック/スコープを走査します。 スコープ識別子を強制すると、ブロックのこのチェックが簡素化されます。 例外を処理するときにスコープを強制することは良い考えのように思えますが、これが通常のコードではなく例外処理の一部であることも示しています。 例外は例外であり、通常は実際に発生させたいものではありませんが、発生する可能性があり、発生したときに処理したいものです。

編集:私が考えることができるもう1つの理由があります、キャッチはELSEとは異なりTRYの後に必須であるということです。 したがって、TRYブロックを定義する明確な方法が必要です。


0


おそらく使い過ぎを思いとどまらせるため。 try-catchブロックは大きくてugいので、使用すると気づくでしょう。 これは、キャッチがアプリケーションのパフォーマンスに与える影響を反映しています。例外のキャッチは、単純なブールテストと比較して非常に遅いです。

一般に、エラーを処理するのではなく、回避する必要があります。 あなたが与える例では、はるかに効率的な方法は使用することです

if(!int.TryParse(s, out i))
 i=0;


0


これを見る別の方法…

「if」、「while」、「for」、および「foreach」ステートメントによって作成された、ベースのないすべての保守問題を考えると、多くの企業は、「ブロック」に作用するステートメントのベースを常に必要とするコーディング標準を持っています。

だから彼らはあなたを書くようにします:

if (itIsSo)
{
ASingleLineOfCode();
}

むしろその後:

if (itIsSo)
ASingleLineOfCode();

(インデントはコンパイラによってチェックされないため、正しいことを当てにすることはできません)

常にベースを必要とする言語を設計するための良いケースがありますが、ベースを常に使用しなければならないため、C#を嫌う人が多すぎます。 ただし、try / catchの場合、ベースを使用せずに逃げることができるとは期待されていなかったため、多くの人に文句を言わずにそれらを要求することができました。

'' '' '

選択を考えると、if / endIf(およびwhile / endWhile)をブロック区切り記号として使用したいのですが、米国はその区切り記号を使用しました。 (Cは、Module2ではなく、ほとんどの言語の外観を定義するようになりました。結局、私たちが行うことのほとんどは、ロジックではなく履歴によって定義されます)


-3


最も単純な(私が思うに)答えは、C / C ++ / C#のコードの各ブロックには中括弧が必要だということです。

*編集#1 *

MSDNから直接の反対票への応答:

try-catchステートメントは、try * block *とそれに続く1つ以上のcatch句で構成され、さまざまな例外のハンドラーを指定します。 _ _

定義によると、それはブロックなので、中括弧が必要です。 それが、 `{}`なしでは使用できない理由です。