Почему переменные не объявлены в "try" в области видимости в "catch" или "finally"? - PullRequest
126 голосов
/ 18 сентября 2008

В C # и в Java (и, возможно, также в других языках) переменные, объявленные в блоке try, не входят в область действия соответствующих блоков «catch» или «finally». Например, следующий код не компилируется:

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

В этом коде ошибка времени компиляции возникает при ссылке на s в блоке catch, потому что s находится только в области видимости в блоке try. (В Java ошибка компиляции: «s не может быть решена»; в C # это «имя s не существует в текущем контексте».)

Похоже, что общим решением этой проблемы является объявление переменных непосредственно перед блоком 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 # и / или в любых других применимых языках)?

Ответы [ 28 ]

1 голос
/ 18 сентября 2008

Если у вас есть попытка catch, вы по большей части должны знать, какие ошибки она может выдать. Эти классы исключений обычно рассказывают все, что вам нужно об этом исключении. Если нет, вы должны создать собственные классы исключений и передать эту информацию. Таким образом, вам никогда не понадобится получать переменные из блока try, потому что исключение самоочевидно. Поэтому, если вам нужно сделать это много, подумайте о своем дизайне и постарайтесь подумать, если есть какой-то другой способ, что вы можете либо предсказать поступление исключений, либо использовать информацию, полученную из исключений, а затем, возможно, отбросить свой исключение с дополнительной информацией.

0 голосов
/ 23 июля 2013

Вместо локальной переменной может быть объявлено публичное свойство; это также должно избежать другой потенциальной ошибки неназначенной переменной. открытая строка S {get; задавать; }

0 голосов
/ 18 сентября 2008

Ну, если он не выдает ошибку компиляции, и вы можете объявить ее для остальной части метода, тогда не будет способа объявить ее только в рамках try. Это заставляет вас четко указывать, где переменная должна существовать, и не делает предположений.

0 голосов
/ 19 сентября 2008

C # 3.0:

string html = new Func<string>(() =>
{
    string webpage;

    try
    {
        using(WebClient downloader = new WebClient())
        {
            webpage = downloader.DownloadString(url);
        }
    }
    catch(WebException)
    {
        Console.WriteLine("Download failed.");  
    }

    return webpage;
})();
0 голосов
/ 19 сентября 2008

Если мы на мгновение проигнорируем проблему с областью видимости, компилятору пришлось бы работать намного усерднее в ситуации, которая не была четко определена. Хотя это не является невозможным, ошибка определения объема также вынуждает вас, автора кода, понимать смысл написанного вами кода (что строка s может быть нулевой в блоке catch). Если ваш код был допустим, в случае исключения OutOfMemory, s даже не гарантируется выделение слота памяти:

// won't compile!
try
{
    VeryLargeArray v = new VeryLargeArray(TOO_BIG_CONSTANT); // throws OutOfMemoryException
    string s = "Help";
}
catch
{
    Console.WriteLine(s); // whoops!
}

CLR (и, следовательно, компилятор) также заставляет вас инициализировать переменные перед их использованием. В представленном блоке вылова это не может быть гарантировано.

Таким образом, в конечном итоге компилятору приходится выполнять большую работу, что на практике не приносит большой пользы и, вероятно, может сбить людей с толку и заставить их спросить, почему try / catch работает по-другому.

В дополнение к непротиворечивости, не допуская ничего сложного и придерживаясь уже установленной семантики определения объема, используемой во всем языке, компилятор и CLR способны обеспечить большую гарантию состояния переменной внутри блока catch. Что он существует и был инициализирован.

Обратите внимание, что разработчики языка проделали хорошую работу с другими конструкциями, такими как , используя и lock , где проблема и область действия четко определены, что позволяет писать более понятный код.

например. с использованием ключевого слова с IDisposable объектов в:

using(Writer writer = new Writer())
{
    writer.Write("Hello");
}

эквивалентно:

Writer writer = new Writer();
try
{        
    writer.Write("Hello");
}
finally
{
    if( writer != null)
    {
        ((IDisposable)writer).Dispose();
    }
}

Если ваш try / catch / finally труден для понимания, попробуйте рефакторинг или введение другого уровня косвенного обращения с промежуточным классом, который инкапсулирует семантику того, что вы пытаетесь выполнить. Не видя реального кода, трудно быть более конкретным.

0 голосов
/ 18 сентября 2008

Если операция присваивания завершится неудачно, ваш оператор catch будет иметь нулевую ссылку на неназначенную переменную.

0 голосов
/ 18 сентября 2008

Я бы подумал, что из-за того, что что-то в блоке try вызвало исключение, его содержимому пространства имен нельзя доверять - т. Е. Ссылка на String 's' в блоке catch может вызвать выброс еще одного исключения.

0 голосов
/ 18 сентября 2008

C # Spec (15.2) заявляет, что "область действия локальной переменной или константы, объявленной в блоке, является блоком."

(в вашем первом примере блок try - это блок, в котором объявлено "s")

...