Co się stanie, jeśli blok finally wyrzuci wyjątek?
Jeśli w końcu blok wyrzuca wyjątek, co dokładnie się dzieje?
W szczególności, co się stanie, jeśli wyjątek zostanie wyrzucony w połowie bloku finally. Czy reszta poleceń (po) w tym bloku zostanie wywołana?
Zdaję sobie sprawę, że wyjątki będą propagować się w górę.
11 answers
Jeśli w końcu blok wyrzuci wyjątek, co Dokładnie się stanie ?
Ten wyjątek rozprzestrzenia się i rośnie, i będzie (może) być obsługiwany na wyższym poziomie.
Twój ostatni blok nie zostanie zakończony poza punktem, w którym wyrzucony jest wyjątek.
Jeśli blok finally był wykonywany podczas obsługi wcześniejszego wyjątku, to ten pierwszy wyjątek zostanie utracony.
Specyfikacja języka C# 4 § 8.9.5: jeśli w końcu blok rzuca inny wyjątek, przetwarzanie bieżącego wyjątku jest zakończone.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-29 12:51:58
W przypadku takich pytań Zwykle otwieram pusty projekt aplikacji konsolowej w Visual Studio i piszę mały przykładowy program:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
catch (Exception ex)
{
Console.WriteLine("Inner catch block handling {0}.", ex.Message);
throw;
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
Po uruchomieniu programu zobaczysz dokładną kolejność wykonywania bloków catch
i finally
. Należy pamiętać, że kod w bloku finally po wyrzuceniu wyjątku nie zostanie wykonany (w rzeczywistości w tym przykładowym programie Visual Studio ostrzega nawet, że wykrył kod nieosiągalny):
Inner catch block handling exception thrown from try block. Inner finally block Outer catch block handling exception thrown from finally block. Outer finally block
Dodatkowe Uwaga
Jak zauważył Michael Damatov, wyjątek z try
bloku zostanie "zjedzony", jeśli nie obsłużysz go w (wewnętrznym) catch
bloku. W rzeczywistości w powyższym przykładzie wyjątek re-throwed nie pojawia się w zewnętrznym bloku catch. Aby uczynić to jeszcze bardziej przejrzystym, spójrz na poniższą nieznacznie zmodyfikowaną próbkę:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
Jak widać na wyjściu wewnętrzny wyjątek jest "utracony" (tzn. ignorowany):
Inner finally block Outer catch block handling exception thrown from finally block. Outer finally block
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-26 09:38:39
Jeśli istnieje wyjątek oczekujący (gdy try
blok ma finally
, ale nie catch
), nowy wyjątek zastępuje ten.
Jeśli nie ma wyjątku oczekującego, działa to tak, jak wyrzucenie wyjątku poza blok finally
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-11 12:35:36
Wyjątek jest propagowany.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-26 08:26:06
Szybki (i dość oczywisty) fragment, aby zapisać "oryginalny wyjątek" (wrzucony do bloku try
) i poświęcić "w końcu wyjątek" (wrzucony do bloku finally
), jeśli oryginalny jest dla ciebie ważniejszy:
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
Gdy powyższy kod jest wykonywany, "oryginalny wyjątek" propaguje się w górę stosu wywołań, a "w końcu wyjątek" jest tracony.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-09 22:55:43
Musiałem to zrobić, aby złapać błąd podczas próby zamknięcia strumienia, który nigdy nie został otwarty z powodu wyjątku.
errorMessage = string.Empty;
try
{
byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);
webRequest = WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "text/xml;charset=utf-8";
webRequest.ContentLength = requestBytes.Length;
//send the request
using (var sw = webRequest.GetRequestStream())
{
sw.Write(requestBytes, 0, requestBytes.Length);
}
//get the response
webResponse = webRequest.GetResponse();
using (var sr = new StreamReader(webResponse.GetResponseStream()))
{
returnVal = sr.ReadToEnd();
sr.Close();
}
}
catch (Exception ex)
{
errorMessage = ex.ToString();
}
finally
{
try
{
if (webRequest.GetRequestStream() != null)
webRequest.GetRequestStream().Close();
if (webResponse.GetResponseStream() != null)
webResponse.GetResponseStream().Close();
}
catch (Exception exw)
{
errorMessage = exw.ToString();
}
}
Jeśli webRequest został utworzony, ale wystąpił błąd połączenia podczas
using (var sw = webRequest.GetRequestStream())
Wtedy wreszcie złapał wyjątek próbujący zamknąć połączenia, które uważał za otwarte, ponieważ webRequest został utworzony.
Jeśli finally didnt have a try-catch inside, ten kod spowoduje nieobsługiwany wyjątek podczas czyszczenia webRequest
if (webRequest.GetRequestStream() != null)
Stamtąd kod zakończyłby się bez poprawnej obsługi błędu, który się wydarzył, a tym samym powodowania problemów z wywołaniem metody.
Mam nadzieję, że to pomoże jako przykład
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-10 05:59:10
Wyrzucenie wyjątku, gdy inny wyjątek jest aktywny, spowoduje, że pierwszy wyjątek zostanie zastąpiony drugim (późniejszym) wyjątkiem.
Oto kod, który ilustruje to, co się dzieje:
public static void Main(string[] args)
{
try
{
try
{
throw new Exception("first exception");
}
finally
{
//try
{
throw new Exception("second exception");
}
//catch (Exception)
{
//throw;
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
- Uruchom kod, a zobaczysz "drugi wyjątek"
- odkomentuj instrukcje try and catch, a zobaczysz "pierwszy wyjątek"
- również odkomentuj polecenie throw;, a zobaczysz" drugi wyjątek".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-29 06:25:56
Kilka miesięcy temu też miałem do czynienia z czymś takim,
private void RaiseException(String errorMessage)
{
throw new Exception(errorMessage);
}
private void DoTaskForFinally()
{
RaiseException("Error for finally");
}
private void DoTaskForCatch()
{
RaiseException("Error for catch");
}
private void DoTaskForTry()
{
RaiseException("Error for try");
}
try
{
/*lacks the exception*/
DoTaskForTry();
}
catch (Exception exception)
{
/*lacks the exception*/
DoTaskForCatch();
}
finally
{
/*the result exception*/
DoTaskForFinally();
}
Aby rozwiązać taki problem zrobiłem klasę użyteczności jak
class ProcessHandler : Exception
{
private enum ProcessType
{
Try,
Catch,
Finally,
}
private Boolean _hasException;
private Boolean _hasTryException;
private Boolean _hasCatchException;
private Boolean _hasFinnallyException;
public Boolean HasException { get { return _hasException; } }
public Boolean HasTryException { get { return _hasTryException; } }
public Boolean HasCatchException { get { return _hasCatchException; } }
public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
public Dictionary<String, Exception> Exceptions { get; private set; }
public readonly Action TryAction;
public readonly Action CatchAction;
public readonly Action FinallyAction;
public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
{
TryAction = tryAction;
CatchAction = catchAction;
FinallyAction = finallyAction;
_hasException = false;
_hasTryException = false;
_hasCatchException = false;
_hasFinnallyException = false;
Exceptions = new Dictionary<string, Exception>();
}
private void Invoke(Action action, ref Boolean isError, ProcessType processType)
{
try
{
action.Invoke();
}
catch (Exception exception)
{
_hasException = true;
isError = true;
Exceptions.Add(processType.ToString(), exception);
}
}
private void InvokeTryAction()
{
if (TryAction == null)
{
return;
}
Invoke(TryAction, ref _hasTryException, ProcessType.Try);
}
private void InvokeCatchAction()
{
if (CatchAction == null)
{
return;
}
Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
}
private void InvokeFinallyAction()
{
if (FinallyAction == null)
{
return;
}
Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
}
public void InvokeActions()
{
InvokeTryAction();
if (HasTryException)
{
InvokeCatchAction();
}
InvokeFinallyAction();
if (HasException)
{
throw this;
}
}
}
I używane w ten sposób
try
{
ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
handler.InvokeActions();
}
catch (Exception exception)
{
var processError = exception as ProcessHandler;
/*this exception contains all exceptions*/
throw new Exception("Error to Process Actions", exception);
}
Ale jeśli chcesz użyć parametrów i zwracać typy, to jest inna historia
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-03 17:54:17
public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}
Sposób obsługi wyjątków rzucanych przez CodeA i CodeB jest taki sam.
Wyjątek rzucony w bloku finally
nie ma nic specjalnego, traktuj go jako wyjątek rzucony kodem B.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-13 20:01:17
Wyjątek propaguje się i powinien być obsługiwany na wyższym poziomie. Jeśli wyjątek nie jest obsługiwany na wyższym poziomie, aplikacja ulega awarii. Wykonanie bloku "finally" zatrzymuje się w punkcie, w którym wyrzucany jest wyjątek.
Niezależnie od tego, czy istnieje wyjątek, czy nie, blok" finally " jest gwarantowany do wykonania.
-
Jeśli blok "finally" jest wykonywany po wystąpieniu wyjątku w bloku try,
-
A jeśli ten wyjątek nie jest obsługiwane
-
I jeśli ostatecznie blok wyrzuci wyjątek
Wtedy oryginalny wyjątek, który wystąpił w bloku try zostanie utracony.
public class Exception
{
public static void Main()
{
try
{
SomeMethod();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void SomeMethod()
{
try
{
// This exception will be lost
throw new Exception("Exception in try block");
}
finally
{
throw new Exception("Exception in finally block");
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-16 17:09:57
Rzuca wyjątek ;) możesz złapać ten wyjątek w innej klauzuli catch.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-26 08:26:21