Почему переменные не объявляются в “попытке” в объеме в “выгоде” или “наконец”?

Просто для полноты: если значения в A не отрицательны и достаточно малы:

lookup = np.empty((np.max(A) + 1), dtype=int)
lookup[A] = np.arange(len(A))
indices  = lookup[B]
129
задан Jon Schneider 26 September 2008 в 01:51
поделиться

26 ответов

Две вещи:

  1. Обычно Java имеет всего 2 уровня объема: глобальный и функция. Но, попытка/выгода является исключением (никакая предназначенная игра слов). Когда исключение выдается, и объект исключения присвоил переменную ему, та переменная объекта только доступна в разделе "выгоды" и уничтожается, как только выгода завершается.

  2. (и что еще более важно). Вы не можете знать, где в попытке блокируются, исключение было выдано. Это, возможно, было, прежде чем Ваша переменная была объявлена. Поэтому невозможно сказать, какие переменные будут доступны для пункта выгоды/наконец. Рассмотрите следующий случай, где обзор состоит в том, как Вы предположили:

    
    try
    {
        throw new ArgumentException("some operation that throws an exception");
        string s = "blah";
    }
    catch (e as ArgumentException)
    {  
        Console.Out.WriteLine(s);
    }
    

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

164
ответ дан 24 November 2019 в 00:30
поделиться

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;
})();
-1
ответ дан 24 November 2019 в 00:30
поделиться

состояния Спецификации (15.2) C# "Объемом локальной переменной или постоянный объявленный в блоке является блок".

(в Вашем первом примере блок попытки является блоком, где "s" объявляется)

0
ответ дан 24 November 2019 в 00:30
поделиться

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

-1
ответ дан 24 November 2019 в 00:30
поделиться

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

0
ответ дан 24 November 2019 в 00:30
поделиться

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

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

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

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

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

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

, например, использование ключевое слово с IDisposable возражает в:

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

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

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

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

0
ответ дан 24 November 2019 в 00:30
поделиться

В Python они видимы в блоках выгоды/наконец, если строка, объявляя их не бросила.

1
ответ дан 24 November 2019 в 00:30
поделиться

Моя мысль была бы то, что, потому что что-то в блоке попытки инициировало исключение, его содержанию пространства имен нельзя доверять - т.е. ссылка на Строку' в блоке выгоды, мог вызвать бросок еще одного исключения.

0
ответ дан 24 November 2019 в 00:30
поделиться

В то время как в Вашем примере странно, что это не работает, берет это подобное:

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

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

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

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

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

{
     String s;
     try
     {
          s = "test";
          //More code
     }
     catch
     {
          Console.WriteLine(s);
     }
}
1
ответ дан 24 November 2019 в 00:30
поделиться

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

, Если это - простая переменная, затем почему Вы заботитесь, какой длины это будет в объеме? Дело не в этом большой соглашение.

в C#, если это - комплексная переменная, Вы захотите реализовать IDisposable. Можно затем или использовать попытку/выгоду/наконец и назвать obj. Расположите () в наконец блок. Или можно использовать ключевое слово использования, которое автоматически назовет Расположение в конце секции кода.

1
ответ дан 24 November 2019 в 00:30
поделиться

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

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

1
ответ дан 24 November 2019 в 00:30
поделиться

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

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

1
ответ дан 24 November 2019 в 00:30
поделиться

Когда у Вас есть выгода попытки, Вы должны в большей части части знать, что ошибки, которые она могла бы бросить. Классы исключений Theese обычно говорят всему, что Вам нужно об исключении. В противном случае необходимо сделать, Вы - собственные классы исключений и проводите ту информацию. Тем путем Вы никогда не должны будете получать переменные из блока попытки, потому что Исключение сам explainatory. Таким образом, если необходимо сделать это много, думайте о, Вы - дизайн и пытаетесь думать, существует ли некоторый другой путь, что можно или предсказать прибытие исключений, или использовать информацию, прибывающую из исключений, и затем возможно, повторно бросить собственное исключение с большей информацией.

1
ответ дан 24 November 2019 в 00:30
поделиться

Поскольку блок попытки и блок выгоды являются 2 различными блоками.

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

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

{ // block B
  Console.Out.WriteLine(s); // or printf or whatever
}
2
ответ дан 24 November 2019 в 00:30
поделиться

Как Вы могли быть уверены, что Вы достигли раздела описаний в своем блоке выгоды? Что, если инстанцирование выдает исключение?

55
ответ дан 24 November 2019 в 00:30
поделиться

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

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

5
ответ дан 24 November 2019 в 00:30
поделиться

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

8
ответ дан 24 November 2019 в 00:30
поделиться

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

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

9
ответ дан 24 November 2019 в 00:30
поделиться

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

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

РЕДАКТИРОВАНИЕ: Я предполагаю, что каждое правило делает , имеют исключение. Следующим является допустимый C++:

int f() { return 0; }

void main() 
{
    int y = 0;

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

объемом x является условное выражение, тогдашний пункт и выражение else.

19
ответ дан 24 November 2019 в 00:30
поделиться

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

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

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

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

1
ответ дан 24 November 2019 в 00:30
поделиться

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

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

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

        throw new Exception(":(")
    }
    catch (Exception e)
    {
        Debug.WriteLine(s);
    }
}
6
ответ дан 24 November 2019 в 00:30
поделиться

Согласно разделу, названному, "Как Бросить и Поймать Исключения" на Уроке 2 из Рассчитанный на индивидуальную скорость обучения Учебный Набор MCTS (Экзамен 70-536): MicrosoftВ®.NET Framework 2.0— Основа Разработки приложений , причина состоит в том, что исключение, возможно, произошло перед объявлениями переменной в блоке попытки (поскольку другие уже отметили).

Кавычка от страницы 25:

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

4
ответ дан 24 November 2019 в 00:30
поделиться

Ответ, как все указали, в значительной степени, "это - то, как блоки определяются".

существуют некоторые предложения сделать код более симпатичным. См. , закрытия 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
}

ОБНОВЛЕНИЕ: ARM реализован в Java 7. http://download.java.net/jdk7/docs/technotes/guides/language/try-with-resources.html

4
ответ дан 24 November 2019 в 00:30
поделиться

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

Это просто должно работать отдельными объемами.

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
ответ дан 24 November 2019 в 00:30
поделиться

Переменные являются блочным уровнем и ограниченный тем блоком Попытки или Выгоды. Подобный определению переменной в, если оператор. Думайте об этой ситуации.

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

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

2
ответ дан 24 November 2019 в 00:30
поделиться

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

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

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

4
ответ дан 24 November 2019 в 00:30
поделиться