Delphi Exception Handling - Как правильно очистить? - PullRequest
10 голосов
/ 19 марта 2010

Я смотрю какой-то код в нашем приложении и наткнулся на что-то немного странное из того, что я обычно делаю. С обработкой и очисткой исключений мы (как я уверен, как и многие другие программисты) используем блок Try / finally, встроенный в блок Try / Except. Теперь я привык к Try / Except внутри Try / Наконец, вот так:

Try
  Try
    CouldCauseError(X);
  Except
    HandleError;
  end;
Finally
  FreeAndNil(x);
end;

но этот другой блок кода перевернут следующим образом:

Try
  Try
    CouldCauseError(X);
  Finally
    FreeAndNil(x);
  end;
Except
  HandleError;
end;

Оглядываясь в Интернете, я вижу, как люди делают это в обоих направлениях, без объяснения причин. Мой вопрос: имеет ли значение, какой блок получает внешний блок, а какой - внутренний блок? Или будут обрабатываться секции исключений и, наконец, независимо от того, как они структурированы? Спасибо.

Ответы [ 4 ]

5 голосов
/ 19 марта 2010

Единственное отличие состоит в том, что try..finally..except потенциально уязвимо для ситуации маскирования исключений.

Представьте себе, что исключение происходит в CouldCauseError () . Затем представьте, что попытка FreeAndNIL (X) в finally вызывает еще одно исключение. Исходное исключение (вполне возможно, которое приводит к нестабильности, ведущей к исключению FreeAndNIL ()) теряется. Обработчик , за исключением , теперь обрабатывает «нисходящее» исключение, возникшее после исходного.

try..except..finally , конечно, избегает этого и должен быть предпочтительным по этой причине (иметь дело с исключениями как можно ближе к их источнику).

Другой способ обработки простого случая, такого как этот (очищаемый объект), состоит в том, чтобы включить очистку как в обычный поток , так и в обработчике исключений:

try
  CouldCauseError(X);
  FreeAndNil(x);
except
  HandleError;
  FreeAndNil(x);
end;

Поначалу это выглядит немного страшно («Мне нужно быть уверенным, что вызывается FreeAndNIL (X), поэтому я ДОЛЖЕН ИМЕТЬ FINALLY !!»), но единственный способ, которым первый FreeAndNIL () может быть не вызван, - это если есть исключение и если равно исключением, то вы в любом случае также FreeAndNIL (), и это делает порядок очистки в событии исключения немного яснее (в смысле удаления шума, который в некоторой степени должен быть «отфильтрован», чтобы понять, что происходит).

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

Однако, это зависит от того факта, что FreeAndNIL () на самом деле " NILThenFree ()" ... X равен NIL, прежде чем это Free'd, поэтому если исключение возникает в FreeAndNIL (X) в обычном потоке, то X будет NIL, когда обработчик исключений перехватит исключение, вызванное X.Free , поэтому он не будет пытаться "дважды освободить" X.

Что бы вы ни решили, надеюсь, это поможет.

4 голосов
/ 19 марта 2010

И наконец, и за исключением, оба сработают, заказ до вас. Это зависит от того, что вы хотите сделать в вашем блоке finally или кроме него. Вы хотите освободить то, что используется в блоке исключений? Разместите наконец вокруг блока исключений.

2 голосов
/ 19 марта 2010

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

Все это означает, что иногда у вас может быть даже такой код:

  try
    try
      try
        CouldCauseError(X);
      except
        HandleErrorWith(X);
      end;
    finally
      FreeAndNil(X); // and/or any resource cleanup
    end;
  except
    CatchAllError;
  end;
0 голосов
/ 19 марта 2010

Сначала ваш код выглядит немного странно. Я скучаю по созданию X.

X := CreateAnX
try
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

Это важно. Потому что, если у вас есть такой код

<code>// don't do something like this
try
  X := CreateAnX
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

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

Альтернативой может быть

<code>X := nil;
try
  X := CreateAnX
  DoSomeThing(X);
finally
  FreeAndNil(x);
end;

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

try
  X := CreateAnX
  try
    DoSomeThing(X);
  finally
    FreeAndNil(x);
  end;
except
  on e: Exception do
    LogException(e)
end;      

Но всегда думайте об этом, когда хотите отловить все ошибки. В качестве примера (и я часто вижу это неправильно) не делайте этого в обработчике OnExecute Indy таким образом. Там вы должны использовать что-то вроде этого

try
  X := CreateAnX
  try
    DoSomeThing(X);
  finally
    FreeAndNil(x);
  end;
except
  on EIdException do
    raise;
  on e: Exception do
    LogException(e)
end;      

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

X := CreateAnX
try
  DoSomeThing(X);
  try
    i := StrToInt(X.Text);
  except
    on EConvertError do
      i := 0;
  end;       
finally
  FreeAndNil(x);
end;

не делай так

X := CreateAnX
try
  try
    DoSomeThing(X);
    i := StrToInt(X.Text);
  except
    on EConvertError do
      i := 0;
  end;       
finally
  FreeAndNil(x);
end;

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

По крайней мере, попытайтесь, за исключением обработки исключений, а не их сокрытия.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...