Используя переменную, объявленную в try, используя значение в catch [duplicate]

Это не лучший подход для таких изменений с циклом набора записей.

, например

UPDATE tblGuardians
SET FirstName = Trim(StrConv(FirstName, 3))
WHERE StrComp(FirstName, Trim(StrConv(FirstName, 3)), 0) <> 0

, и то же самое для LastName.

Используется вместо StrComp вместо простого <>, потому что последнее нечувствительно к регистру. Третий параметр 0 = vbBinaryCompare.

112
задан Jon Schneider 26 September 2008 в 02:51
поделиться

28 ответов

Две вещи:

  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 не будет объявлен. Учитывая, что уловы предназначены для обработки исключительных обстоятельств, а finallys должен выполнять , будучи безопасным и объявляет, что проблема во время компиляции намного лучше, чем во время выполнения.

142
ответ дан Krinkle 19 August 2018 в 00:03
поделиться
  • 1
    Привет, ваше объяснение совершенно правильно. Однако почему try / catch определяется так, как есть? Почему бы не определить try catch с вложенным блоком catch / finally, т. Е. Попробовать {... catch {...} finally {...}}. (Компилятор должен будет переместить объявления переменных в начало блока, чтобы они были доступны в catch. – Dirk Vollmar 29 November 2008 в 11:31
  • 2
    На скомпилированном языке (например, Java или C #), если переменная находится в области видимости, не имеет значения, где она объявлена ​​- она ​​доступна в другом месте в этой области. – dkretz 9 December 2008 в 03:00
  • 3
    @divo, даже если вы измените код Джона с тем, который вы предлагаете, у вас все еще есть та же проблема. ;) попробуйте {throw [...]; строка s = "blah"; catch {[...]} наконец {Write (s)}}; – pek 5 July 2009 в 21:59
  • 4
    Чтобы прояснить комментарий @le dorfier: vars доступны из объявления до конца области, не обязательно от начала блока до конца блока. Итак, foo = 1; int foo; & quot; вызывает ошибку. – Pops 17 February 2010 в 20:59
  • 5
    Когда дело доходит до этих вещей, в большинстве случаев язык правильный. – CurtainDog 27 April 2010 в 07:01

Как вы могли быть уверены, что достигли части объявления в своем блоке catch? Что делать, если экземпляр создает исключение?

52
ответ дан Burkhard 19 August 2018 в 00:03
поделиться
  • 1
    А? Объявления переменных не генерируют исключений. – Joshua 9 December 2008 в 03:11
  • 2
    Согласитесь, это экземпляр, который может вызвать исключение. – Burkhard 5 July 2009 в 18:48

Как было указано другими пользователями, фигурные скобки определяют область видимости почти в каждом языке стиля С., о котором я знаю.

Если это простая переменная, то почему вас это волнует, будет в сфере охвата? Это не так уж важно.

в C #, если это сложная переменная, вам нужно реализовать IDisposable. Затем вы можете использовать try / catch / finally и вызвать obj.Dispose () в блоке finally. Или вы можете использовать ключевое слово using, которое автоматически вызовет Dispose в конце раздела кода.

1
ответ дан Charles Graham 19 August 2018 в 00:03
поделиться

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
ответ дан core 19 August 2018 в 00:03
поделиться
  • 1
    WTF? Почему при голосовании? Инкапсуляция является неотъемлемой частью ООП. Выглядит тоже. – core 20 January 2009 в 19:08
  • 2
    Согласен, что с этим не так? Я хотел бы знать! – Dave Hillier 13 January 2010 в 11:43
  • 3
    Я не был нисходящим, но в чем-то неверно возвращать неинициализированную строку. – Ben Voigt 27 April 2010 в 06:33

Как указано ravenspoint, все ожидают, что переменные будут локальными для блока, в котором они определены. try вводит блок, а также catch.

Если вы хотите, чтобы переменные были локальными для обоих try и catch, попробуйте включить оба блока:

// here is some code
{
    string s;
    try
    {

        throw new Exception(":(")
    }
    catch (Exception e)
    {
        Debug.WriteLine(s);
    }
}
4
ответ дан Daren Thomas 19 August 2018 в 00:03
поделиться

Простой ответ: C и большинство языков, унаследовавших его синтаксис, являются блочными. Это означает, что если переменная определена в одном блоке, т. Е. Внутри {}, то есть в ее области.

Исключением, кстати, является JavaScript, который имеет похожий синтаксис, но является областью действия функции , В JavaScript переменная, объявленная в блоке try, находится в области видимости в блоке catch и везде в ее содержащей функции.

5
ответ дан dgvid 19 August 2018 в 00:03
поделиться

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

Он просто должен работать как отдельные области.

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
2
ответ дан EndangeredMassa 19 August 2018 в 00:03
поделиться

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

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

EDIT: Я думаю, что каждое правило имеет исключение. Допустим, что C ++:

int f() { return 0; }

void main() 
{
    int y = 0;

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

Область действия x является условным, предложением then и предложением else.

17
ответ дан Ferruccio 19 August 2018 в 00:03
поделиться

Поскольку блок try и блок catch являются 2 разными блоками.

В следующем коде вы ожидали бы, что s, определенный в блоке A, будет видимым в блоке B?

{ // block A
  string s = "dude";
}

{ // block B
  Console.Out.WriteLine(s); // or printf or whatever
}
2
ответ дан François 19 August 2018 в 00:03
поделиться

Согласно разделу «Как выбрасывать и выхватывать исключения» в уроке 2 учебного комплекта для самостоятельного обучения MCTS (экзамен 70-536): Microsoft® .NET Framework 2.0-Application Development Foundation, причина в том, что исключение возможно, произошли до объявления переменных в блоке try (как уже отмечали другие).

Цитата со страницы 25:

"Обратите внимание, что объявление StreamReader было перемещено за пределы блока Try в предыдущем примере это необходимо, потому что блок finally не может получить доступ к переменным, объявленным в блоке Try. Это имеет смысл, поскольку в зависимости от того, где произошло исключение, объявления переменных в блоке Try могут еще не выполняться ".

3
ответ дан hurst 19 August 2018 в 00:03
поделиться

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

1
ответ дан Jesper Blad Jensen 19 August 2018 в 00:03
поделиться

Все остальные подняли основы - что происходит в блоке, остается в блоке. Но в случае .NET может оказаться полезным изучить, что думает компилятор. Возьмем, к примеру, следующий код try / catch (обратите внимание, что StreamReader объявлен правильно за пределами блоков):

static void TryCatchFinally()
{
    StreamReader sr = null;
    try
    {
        sr = new StreamReader(path);
        Console.WriteLine(sr.ReadToEnd());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
    finally
    {
        if (sr != null)
        {
            sr.Close();
        }
    }
}

В MSIL будет скомпилировано что-то похожее на следующее:

.method private hidebysig static void  TryCatchFinallyDispose() cil managed
{
  // Code size       53 (0x35)    
  .maxstack  2    
  .locals init ([0] class [mscorlib]System.IO.StreamReader sr,    
           [1] class [mscorlib]System.Exception ex)    
  IL_0000:  ldnull    
  IL_0001:  stloc.0    
  .try    
  {    
    .try    
    {    
      IL_0002:  ldsfld     string UsingTest.Class1::path    
      IL_0007:  newobj     instance void [mscorlib]System.IO.StreamReader::.ctor(string)    
      IL_000c:  stloc.0    
      IL_000d:  ldloc.0    
      IL_000e:  callvirt   instance string [mscorlib]System.IO.TextReader::ReadToEnd()
      IL_0013:  call       void [mscorlib]System.Console::WriteLine(string)    
      IL_0018:  leave.s    IL_0028
    }  // end .try
    catch [mscorlib]System.Exception 
    {
      IL_001a:  stloc.1
      IL_001b:  ldloc.1    
      IL_001c:  callvirt   instance string [mscorlib]System.Exception::ToString()    
      IL_0021:  call       void [mscorlib]System.Console::WriteLine(string)    
      IL_0026:  leave.s    IL_0028    
    }  // end handler    
    IL_0028:  leave.s    IL_0034    
  }  // end .try    
  finally    
  {    
    IL_002a:  ldloc.0    
    IL_002b:  brfalse.s  IL_0033    
    IL_002d:  ldloc.0    
    IL_002e:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()    
    IL_0033:  endfinally    
  }  // end handler    
  IL_0034:  ret    
} // end of method Class1::TryCatchFinallyDispose

Что мы видим? MSIL уважает блоки - они являются неотъемлемой частью базового кода, сгенерированного при компиляции вашего C #. Объем не просто установлен в спецификации C #, но и в спецификации CLR и CLS.

Объем защищает вас, но вам иногда приходится обходить его. Со временем вы привыкаете к этому, и он начинает чувствовать себя естественным. Как и все остальные, что происходит в блоке, остается в этом блоке. Вы хотите поделиться чем-то? Вы должны выйти за пределы блоков ...

8
ответ дан John Rudy 19 August 2018 в 00:03
поделиться

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

0
ответ дан jpbarto 19 August 2018 в 00:03
поделиться

Переменные являются уровнем блока и ограничены этим блоком Try или Catch. Аналогично определению переменной в выражении if. Подумайте об этой ситуации.

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

Строка никогда не будет объявлена, поэтому на нее нельзя полагаться.

2
ответ дан jW. 19 August 2018 в 00:03
поделиться

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

0
ответ дан kemiller2002 19 August 2018 в 00:03
поделиться

Хотя в вашем примере странно, что это не сработает, возьмите этот схож:

    try
    {
         //Code 1
         String s = "1|2";
         //Code 2
    }
    catch
    {
         Console.WriteLine(s.Split('|')[1]);
    }

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

Опять это теоретически можно было бы зафиксировать, разрешив только разделенные определения (String s; s = "1|2";) или какой-либо другой набор условий, но, как правило, проще сказать «нет».

Кроме того, это позволяет определять семантику области видимости в глобальном масштабе без исключения, в частности, местные жители сохраняются до тех пор, пока {} они определены во всех случаях. Малая точка, но точка.

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

{
     String s;
     try
     {
          s = "test";
          //More code
     }
     catch
     {
          Console.WriteLine(s);
     }
}
1
ответ дан Lucas 19 August 2018 в 00:03
поделиться

В C ++, во всяком случае, область автоматической переменной ограничена фигурными фигурными скобками, которые ее окружают. Почему кто-то ожидает, что это будет отличаться, перелистывая ключевое слово try за фигурные скобки?

8
ответ дан ravenspoint 19 August 2018 в 00:03
поделиться
  • 1
    Согласовано; & Quot;} & Quot; означает конечный охват. Тем не менее, try-catch-finally необычен тем, что после блока try вы must имеете catch и / или, наконец, блок; таким образом, исключение из нормального правила, в котором область действия блока try, перенесенного в ассоциированный catch / finally, может показаться приемлемой? – Jon Schneider 18 September 2008 в 21:30

Что делать, если исключение создается в некотором коде, который выше объявления переменной. Это означает, что сама декларация не была в этом случае.

try {

       //doSomeWork // Exception is thrown in this line. 
       String s;
       //doRestOfTheWork

} catch (Exception) {
        //Use s;//Problem here
} finally {
        //Use s;//Problem here
}
1
ответ дан Ravi 19 August 2018 в 00:03
поделиться

Если на какой-то момент мы не будем игнорировать проблему контрольного блока, то в ситуации, которая недостаточно четко определена, сложнее будет работать намного сложнее. Хотя это не невозможно, ошибка определения области охвата также заставляет вас, автора кода, реализовать импликацию кода, который вы пишете (что строка 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 (и, следовательно, компилятор) также заставляет вас инициализировать переменные прежде чем они будут использованы. В представленном блоке catch он не может этого гарантировать.

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

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

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

например с использованием ключевого слова с объектами 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 / наконец-то трудно понять, попробуйте рефакторинг или введите другой слой косвенности с промежуточным классом, который инкапсулирует семантику того, что вы пытаетесь выполнить. Не видя реального кода, трудно быть более конкретным.

0
ответ дан Robert Paulson 19 August 2018 в 00:03
поделиться

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

0
ответ дан SaaS Developer 19 August 2018 в 00:03
поделиться
  • 1
    Он не назначен. Это даже не значение null (в отличие от экземпляров и статических переменных). – Tom Hawtin - tackline 19 September 2008 в 10:20

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

Но в целом выражения инициализации могут генерировать исключения. Это не имело бы смысла для переменной, инициатор которой выбрал исключение (или которое было объявлено после другой переменной, где это произошло), чтобы быть в области catch / finally.

Кроме того, пострадает читаемость кода. Правило в C (и последующие языки, включая C ++, Java и C #) просты: области переменных следуют за блоками.

Если вы хотите, чтобы переменная была в области try / catch / finally, но нигде else, затем заверните все это в другой набор фигурных скобок (пустой блок) и объявите переменную перед попыткой.

1
ответ дан Steve Jessop 19 August 2018 в 00:03
поделиться

Функция C # Spec (15.2) утверждает: «Область локальной переменной или константы, объявленной в блоке, является блоком».

(в первом примере блок try это блок, в котором объявлено «s»)

С уважением, tamberg

0
ответ дан tamberg 19 August 2018 в 00:03
поделиться

@burkhard имеет вопрос, почему правильно ответил, но в качестве примечания я хотел добавить, в то время как ваш рекомендуемый пример решения хорош 99.9999 +% времени, это не очень хорошая практика, гораздо безопаснее либо проверить null, прежде чем использовать что-либо, созданное в блоке try, или инициализировать переменную чем-то вместо того, чтобы просто объявлять ее перед блоком try. Например:

string s = String.Empty;
try
{
    //do work
}
catch
{
   //safely access s
   Console.WriteLine(s);
}

Или:

string s;
try
{
    //do work
}
catch
{
   if (!String.IsNullOrEmpty(s))
   {
       //safely access s
       Console.WriteLine(s);
   }
}

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

4
ответ дан Timothy Carter 19 August 2018 в 00:03
поделиться

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

0
ответ дан usefulBee 19 August 2018 в 00:03
поделиться

В Python они видны в блоках catch / finally, если строка, объявляющая их, не выбрасывает.

1
ответ дан user 19 August 2018 в 00:03
поделиться

Когда вы объявляете локальную переменную, она помещается в стек (для некоторых типов все значение объекта будет в стеке, для других типов в стек будет только ссылка). Когда в блоке try есть исключение, локальные переменные в блоке освобождаются, что означает, что стек «разворачивается» обратно в состояние, в котором он находился в начале блока try. Это по дизайну. Это то, как try / catch способен отступать от всех вызовов функций внутри блока и возвращает вашу систему в функциональное состояние. Без этого механизма вы никогда не сможете быть уверены в состоянии чего-либо, когда возникает исключение.

Наличие вашего кода обработки ошибок зависит от внешних объявленных переменных, которые имеют свои значения, измененные внутри блока try, кажется мне плохим дизайном , То, что вы делаете, по сути направляет ресурсы намеренно, чтобы получить информацию (в данном случае это не так уж плохо, потому что вы только утечка информации, но представьте, если бы это был какой-то другой ресурс? Вы просто делаете жизнь сложнее на себе в будущее). Я бы предложил разбить ваши блоки try на более мелкие куски, если вам нужна более гранулированность при обработке ошибок.

1
ответ дан Wedge 19 August 2018 в 00:03
поделиться

Ответ, как отмечали все, в значительной степени «вот как определяются блоки».

Есть несколько предложений, чтобы сделать код более красивым. См. ARM

 try (FileReader in = makeReader(), FileWriter out = makeWriter()) {
       // code using in and out
 } catch(IOException e) {
       // ...
 }

Предполагается, что исправления также будут устранены.

with(FileReader in : makeReader()) with(FileWriter out : makeWriter()) {
    // code using in and out
}

UPDATE: ARM реализована в Java 7. http://download.java.net/jdk7/docs/technotes/guides/language/try-with-resources.html

4
ответ дан ykaganovich 19 August 2018 в 00:03
поделиться

Часть причин, по которым они не находятся в той же области, состоит в том, что в любой точке блока try вы можете исключить исключение. Если они были в одном и том же объеме, это была катастрофа в ожидании, потому что в зависимости от того, где было выбрано исключение, это может быть еще более неоднозначным.

По крайней мере, когда объявлено за пределами блока try, вы знаете наверняка, какая переменная могла бы быть минимальной, когда генерируется исключение; Значение переменной перед блоком try.

1
ответ дан zxcv 19 August 2018 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: