Считается плохой практикой или существует какой-либо недостаток с использованием try / finally try / except вместо begin / end?

Во многих местах в некоторых приложениях, которые я поддерживаю, я нашел код, который использует блок try/finally или try/except в предложении for loop или if, избегая использования начала / конца

Рассмотрим следующий код (не производственный код, а просто образец)

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Classes,
  SysUtils;
    
Procedure TestNoBeginEnd;
var
 i   : Integer;
 L1  : TStringList;
begin
    for i := 1 to 10 do
    try
      L1:=TStringList.Create;
      try
        L1.Add('Bar');
        L1.Add(IntToStr(i));
        L1.Add('Foo');
      finally
        Writeln(L1.Text);
        L1.Free;
      end;
    except
      on E: Exception do
        Writeln('Opps '+E.ClassName, ': ', E.Message);
    end;
end;

begin
  try
    TestNoBeginEnd;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

Вопрос: считается ли плохой практикой, запахом кода или наличием каких-либо недостатков с использованием try / finally или try / except вместо begin / end в delphi?

ОБНОВЛЕНИЕ

Прошу прощения за глупый пример кода, просто для пояснения, что try / finally и try / except не претендуют на замену начала / конца, просто чтобы не использовать его (начало / конец), когда существует случай, когда использование of the try / finally или try / except не требует начала / конца.


person Salvador    schedule 31.01.2012    source источник
comment
Если вы хотите защитить ресурс от исключения, используйте try finally. Если вам нужно поймать исключение, используйте try except. Альтернатив нет.   -  person David Heffernan    schedule 31.01.2012


Ответы (4)


Лично я не вижу необходимости добавлять лишний begin..end вокруг try..finally/except..end, особенно когда вероятность того, что код будет добавлен непосредственно перед try или, конечно, будет после end, мала.

При этом вопрос будет больше в том, нужно ли нам систематически begin..end после ..do или ..then, даже если есть только одна инструкция (в вашем случае инструкция try..).

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

Когда задействовано достаточное количество строк, я обычно добавляю begin..end, чтобы сделать обзор более очевидным.

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

person Francesca    schedule 31.01.2012
comment
+1 за абзац «Когда много строк» ​​и следующий за ним. Ясность и ремонтопригодность всегда превыше всего. :) - person Ken White; 31.01.2012

Перво-наперво. Begin / End - это не то же самое, что Try / Etc. Последняя первая обозначает набор инструкций, а вторая - отдельную инструкцию (которая, возможно, содержит множество инструкций).

Итак, они подобны молотку и лестнице. Разные инструменты для разных работ. Конечно, вы забиваете гвоздь лестницей, но это не самый эффективный способ решения проблемы.

Изменить: если я пойму вопрос (теперь, когда вы его отредактировали), вы спрашиваете, можно ли использовать одну попытку / и т. Д. Без начала / конца. Да, вы можете это сделать, потому что try / etc - это один оператор. На самом деле я бы даже сказал, что, вероятно, лучше избегать слишком большого количества бесполезного кода.

Перед редактированием:

Так что это значит? Что ж, это означает, что если в этой функции возникает ошибка, она регистрируется, но по существу игнорируется.

Если нет действительно хорошей обработки исключений и код не передает исключения или восстанавливает их правильно Я бы сказал, что это плохая практика.

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

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

Последний случай я распространяю на все «большие границы системы». то есть я буду собирать исключения на границах службы / API и соответствующим образом регистрировать / переносить.

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

person Preet Sangha    schedule 31.01.2012

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

Тем не менее, при ответе на ваш исходный вопрос:

Операторы Try / finally или Try / Except никогда не должны использоваться, если достаточно оператора Begin / End. Помимо того факта, что операторы Try добавляют (крошечные или даже незаметные) дополнительные инструкции к скомпилированному коду, они подразумевают необходимость кода, которого нет:

for I := 0 to Count - 1 do
try
  Inc(Rect.Left, Shift);
finally
  Inc(Rect.Right, Shift);
end;

Просто не делай этого.

person NGLN    schedule 31.01.2012

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

Принимая во внимание приведенные выше детали, ваш пример несколько неудачен из-за того, что он многократно добавляет ненужные накладные расходы в цикле. Всякий раз, когда вы можете вывести пробные блоки из цикла, это хорошо. В вашем примере, если вы ДОЛЖНЫ иметь возможность перехватывать исключение для каждой итерации цикла, приведенный ниже код будет более эффективным, в противном случае переместите также блок except.

Procedure TestNoBeginEnd;
var
 i   : Integer;
 L1  : TStringList;
begin
  L1:=TStringList.Create;
  try
    for i := 1 to 10 do
    try
      L1.Clear;
      L1.Add('Bar');
      L1.Add(IntToStr(i));
      L1.Add('Foo');
      Writeln(L1.Text);
      end;
    except
      on E: Exception do
        Writeln('Opps '+E.ClassName, ': ', E.Message);
    end;
  finally
    L1.Free;
  end;
end;
person GDF    schedule 31.01.2012