Как мне повторно поднять исключение Delphi после его регистрации?
Знаете ли вы способ блокирования, регистрации и повторного создания исключения в коде Delphi?
Простой пример:
procedure TForm3.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Bum');
except
on E: Exception do
begin
MyHandleException(E);
end;
end;
end;
procedure TForm3.MyHandleException(AException: Exception);
begin
ShowMessage(AException.Message);
LogThis(AException.Message);
// raise AException; - this will access violate
end;
Поэтому мне нужно повторно поднять его в блоке except, но мне было интересно, есть ли лучший способ написать мой собственный метод для обработки и (при определенных условиях) для повторного создания исключений.
Ответы
Ответ 1
Если вы хотите повторно создать исключение только при определенных условиях, напишите
procedure TForm3.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Bum');
except
on E: Exception do
begin
if MyHandleException(E) then
raise;
end;
end;
end;
function TForm3.MyHandleException(AException: Exception): boolean;
begin
ShowMessage(AException.Message);
result := true/false;
end;
Ответ 2
Следуя за запиской Craig Young, я использовал что-то в соответствии с следующим кодом. Вы можете сохранить исходное местоположение исключения, используя идентификатор "at" с функцией ExceptAddr. Также сохраняется исходный тип и информация типа исключения.
procedure MyHandleException(AMethod: string);
var
e: Exception;
begin
e := Exception(AcquireExceptionObject);
e.Message := e.Message + ' raised in ' + AMethod;
raise e at ExceptAddr;
end;
try
...
except
MyHandleException('MyMethod');
end;
Ответ 3
Следующее будет работать, но, конечно, не идеально по двум причинам:
- Исключение возникает из другого места в стеке вызовов.
- Вы не получаете точную копию исключения - особенно те классы, которые добавляют атрибуты. То есть вам придется явно скопировать нужные вам атрибуты.
- Копирование пользовательских атрибутов может стать беспорядочным из-за требуемой проверки типов.
.
procedure TForm3.MyHandleException(AException: Exception);
begin
ShowMessage(AException.Message);
LogThis(AException.Message);
raise ExceptClass(AException.ClassType).Create(AException.Message);
end;
Преимущества заключаются в том, что вы сохраняете исходный класс исключений и сообщение (и любые другие атрибуты, которые вы хотите скопировать).
В идеале вы хотите вызвать System._RaiseAgain
, но, увы, это подпрограмма "компилятор-магия" и может быть вызвана только raise;
.
Ответ 4
Вы можете попробовать использовать (system.pas):
function AcquireExceptionObject: Pointer;
AcquireExceptionObject возвращает указатель на текущий объект исключения и предотвращает освобождение объекта исключения, когда текущий обработчик исключений завершается.
Примечание. Функция AcquireExceptionObject увеличивает счет ссылки на объект исключения. Убедитесь, что счетчик ссылок уменьшен, когда объект исключения больше не нужен. Это происходит автоматически, если вы используете объект исключения для повторного создания исключения. Во всех остальных случаях каждый вызов метода AcquireExceptionObject должен иметь соответствующий вызов ReleaseExceptionObject. Последовательности AcquireExceptionObject/ReleaseExceptionObject могут быть вложенными.
Ответ 5
Вы можете просто использовать команду Raise
для повторного создания исключения:
begin
MyHandleException(E);
Raise;
end;
Ответ 6
Старая тема, но как насчет этого решения?
procedure MyHandleException(AException: Exception);
begin
ShowMessage(AException.Message);
AcquireExceptionObject;
raise AException;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
try
raise Exception.Create('Bum');
except
on E: Exception do
MyHandleException(E);
end;
end;
Это основано на первом коде, опубликованном Эдуардо.
Ответ 7
Вы можете получить объект исключения до вызова обработчика и оставить обработчик самим одним лайнером. Однако у вас все еще есть много бремени "Try/Except/Do/End".
Procedure MyExceptionHandler(AException: Exception);
Begin
Log(AException); // assuming it accepts an exception
ShowMessage(AException.Message);
raise AException; // the ref count will be leveled if you always raise it
End;
Procedure TForm3.Button1Click(Sender: TObject);
Begin
Try
Foo;
Except On E:Exception Do
MyExceptionHandler(Exception(AcquireExceptionObject));
End;
End;
Однако, если вы хотите только избавиться от повторяющегося кода обработки ошибок в обработчиках событий, вы можете попробовать следующее:
Procedure TForm3.ShowException(AProc : TProc);
Begin
Try
AProc;
Except On E:Exception Do Begin
Log(E);
ShowMessage(E.Message);
End; End;
End;
Уменьшение кода обработчика событий:
Procedure TForm3.Button1Click(Sender: TObject);
Begin
ShowException(Procedure Begin // anon method
Foo; // if this call raises an exception, it will be handled by ShowException handler
End);
End;
Вы также можете заставить его работать для функций, используя параметризованные функции:
Function TForm3.ShowException<T>(AFunc : TFunc<T>) : T;
Begin
Try
Result := AFunc;
Except On E:Exception Do Begin
Log(E);
ShowMessage(E.Message);
End; End;
End;
И создание ShowException возвращает значение (действует как passthru):
Procedure TForm3.Button1Click(Sender: TObject);
Var
V : Integer;
Begin
V := ShowException<Integer>(Function : Integer Begin // anon method
Result := Foo; // if this call raises an exception, it will be handled by ShowException handler
End);
End;
Или даже сделать процедуру анона непосредственным касанием внешней переменной (ов) видимости:
Procedure TForm3.Button1Click(Sender: TObject);
Var
V : Integer;
Begin
ShowException(Procedure Begin // anon method
V := Foo; // if this call raises an exception, it will be handled by ShowException handler
End);
End;
Существуют некоторые ограничения на взаимодействие переменных внутри тела анонимной функции и тех, которые определены во внешней области, но для простых случаев, подобных этим, вы будете более чем точными.