Где, наконец, нужно? - PullRequest
       44

Где, наконец, нужно?

9 голосов
/ 11 февраля 2011

Я знаю, как использовать try-catch-finally. Однако я не получаю преимущества от использования finally, поскольку всегда могу разместить код после блока try-catch. Есть ли четкий пример?

Ответы [ 11 ]

4 голосов
/ 11 февраля 2011

Он почти всегда используется для очистки, обычно неявно с помощью оператора using:

FileStream stream = new FileStream(...);
try
{
    // Read some stuff
}
finally
{
    stream.Dispose();
}

Теперь это , а не эквивалентно

FileStream stream = new FileStream(...);
// Read some stuff
stream.Dispose();

потому что код "read some stuff" может вызвать исключение или, возможно, возврат - и как бы он ни завершался, мы хотим избавиться от потока.

Итак, блоки finally обычно предназначены для очистки ресурсов. Однако в C # они обычно неявные с помощью оператора using:

using (FileStream stream = new FileStream(...))
{
    // Read some stuff
} // Dispose called automatically

finally блоки гораздо чаще встречаются в Java, чем в C #, именно из-за оператора using. Я очень редко пишу свои finally блоки в C #.

4 голосов
/ 11 февраля 2011

Вам нужен наконец, потому что вы не всегда должны ловить:

void M()
{
    var fs = new FileStream(...);
    try
    {
       fs.Write(...);
    }
    finally
    {
       fs.Close();
    }
}

Приведенный выше метод не перехватывает ошибки при использовании fs, оставляя их вызывающей стороне. Но это всегда должно закрывать поток.

Обратите внимание, что этот тип кода обычно использует блок using() {}, но это просто сокращение для попытки / наконец. Для завершения:

    using(var fs = new FileStream(...))
    {
       fs.Write(...);
    } // invisible finally here
3 голосов
/ 11 февраля 2011
try 
{
    DoSomethingImportant();
}
finally
{
    ItIsRidiculouslyImportantThatThisRuns();
}

Когда у вас есть блок finally, код в нем гарантированно будет выполняться после выхода из try.Если вы размещаете код вне try / catch, это не так.Более распространенным примером является тот, который используется с одноразовыми ресурсами при использовании оператора using.

using (StreamReader reader = new StreamReader(filename))
{
}

расширяется до

StreamReader reader = null;
try
{
    reader = new StreamReader(filename);
    // do work
}
finally 
{
    if (reader != null)
       ((IDisposable)reader).Dispose();
}

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

* Обратите внимание, что существуют ситуации, когда управление не завершает попытку, и finally не будет фактически выполнено.В качестве простого примера, PowerFailureException.

2 голосов
/ 11 февраля 2011

Обновление : На самом деле это не очень хороший ответ. С другой стороны, может быть, - это хороший ответ, потому что он иллюстрирует прекрасный пример успеха finally, когда разработчик (то есть я) может не обеспечить надлежащую очистку. В приведенном ниже коде рассмотрим сценарий, в котором выдается исключение , отличное от , чем SpecificException. Тогда первый пример все равно будет выполнять очистку, а второй - нет, даже если разработчик может думать"Я поймал исключение и обработал его, поэтому, несомненно, будет выполняться следующий код."


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

try
{
    DoSomethingTricky();
    return true;
}
catch (SpecificException ex)
{
    LogException(ex);
    return false;
}
finally
{
    DoImportantCleanup();
}

Альтернатива вышеприведенному без a finally (на мой взгляд) несколько менее читабельна:

bool success;

try
{
    DoSomethingTricky();
    success = true;
}
catch (SpecificException ex)
{
    LogException(ex);
    success = false;
}

DoImportantCleanup();
return success;

* Я думаю, лучше пример try / catch / finally - это когда исключение вызывается повторно (использование throw, не throw ex - но это уже другая тема) в блоке catch, поэтому необходим finally, так как без него код после try / catch не будет работать. Обычно это выполняется с помощью оператора using для ресурса IDisposable, но это не всегда так. Иногда очистка - это не просто вызов Dispose (или больше, чем просто вызов Dispose).

2 голосов
/ 11 февраля 2011

Код, помещенный в блок finally, выполняется даже в том случае, если:

  • в блоке try или catch *
    ИЛИ * *1007*1010 *
  • блок catch перебрасывает исключение

Пример:

public int Foo()
{
  try
  {
    MethodThatCausesException();
  }
  catch
  {
    return 0;
  }

  // this will NOT be executed
  ReleaseResources();
}

public int Bar()
{
  try
  {
    MethodThatCausesException();
  }
  catch
  {
    return 0;
  }
  finally
  {
    // this will be executed
    ReleaseResources();
  }
}
1 голос
/ 11 февраля 2011

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

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

1 голос
/ 11 февраля 2011

вы не обязательно используете его с исключениями.У вас может быть try/finally для выполнения очистки перед каждым return в блоке.

0 голосов
/ 11 февраля 2011

Например, во время процесса вы можете отключить WinForm ...

try
{
this.Enabled = false;
// some process
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
this.Enabled = true;
}
0 голосов
/ 11 февраля 2011

Если в блоке catch возникает (или перебрасывается) исключение, код после catch не будет выполнен - ​​напротив, код внутри finally все равно будет выполняться.

Кроме того, код внутри finally даже выполняется при выходе из метода с использованием return.

Наконец, это особенно удобно при работе с внешними ресурсами, такими как файлы, которые необходимо закрыть:

Stream file;
try
{
  file = File.Open(/**/);
  //...
  if (someCondition)
     return;
  //...
}
catch (Exception ex)
{
   //Notify the user
}
finally
{
  if (file != null)
    file.Close();
}

Обратите внимание, что в этом примере вы также можете использовать , используя :

using (Stream file = File.Open(/**/))
{
  //Code
}
0 голосов
/ 11 февраля 2011

Я не уверен, как это делается в C #, но в Delphi вы найдете "наконец-то" очень часто.Ключевое слово - ручное управление памятью.

MyObject := TMyObject.Create(); //Constructor
try 
     //do something
finally
    MyObject.Free(); 
end;
...