1. ホーム
  2. c#

[解決済み] try "で宣言された変数が "catch "や "finally "のスコープに入らないのはなぜですか?

2022-04-27 20:25:41

質問

C#やJavaでは(おそらく他の言語でも)、"try"ブロック内で宣言された変数は、対応する"catch"や"final"ブロックでは範囲外になっています。 例えば、次のようなコードはコンパイルできません。

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 cannot be resolved" C#の場合は "The name 's' does not exist in the current context" となります)。

この問題の一般的な解決策は、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-finally のコンテキストのみではなく、メソッドの残りの部分すべて)。

この言語設計の決定には、どのような根拠があったのでしょうか(Java、C#、その他該当する言語において)。

どのように解決するのですか?

2つあります。

  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);
    }
    
    

これは明らかに問題です。例外ハンドラに到達したとき、sは宣言されていないことになります。catchは例外的な状況を処理するためのものであり、finallysは例外的な状況を処理するためのものであることを考えると、これは明らかに問題です。 実行する前に、コンパイル時に安全性を確認し、問題を宣言しておくことは、実行時よりもはるかに良いことです。