Najlepsze praktyki w zakresie połowu i re-throwing.NET wyjątki
Jakie są najlepsze praktyki, które należy wziąć pod uwagę przy wyłapywaniu wyjątków i ich ponownym wyrzucaniu? Chcę się upewnić, że Exception
obiekt InnerException
i ślad stosu są zachowane. Czy istnieje różnica między poniższymi blokami kodu w sposobie, w jaki sobie z tym radzą?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
Vs:
try
{
//some code
}
catch
{
throw;
}
11 answers
Sposobem na zachowanie śladu stosu jest użycie throw;
to jest również ważne
try {
// something that bombs here
} catch (Exception ex)
{
throw;
}
throw ex;
jest to w zasadzie jak rzucenie wyjątku od tego punktu, więc ślad stosu trafi tylko do miejsca, w którym wydajesz throw ex;
oświadczenie.
Mike jest również poprawny, zakładając, że wyjątek pozwala przekazać wyjątek (co jest zalecane).
Karl Seguin mA świetny wpis na temat obsługi wyjątków w swoich fundamentach programowanie e-booka, co jest świetną lekturą.
Edit: link roboczy do Podstawy programowania pdf. Po prostu wyszukaj w tekście "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
2017-09-23 13:06:41
Jeśli rzucisz nowy wyjątek z pierwszym wyjątkiem, zachowasz również początkowy ślad stosu..
try{
}
catch(Exception ex){
throw new MoreDescriptiveException("here is what was happening", ex);
}
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
2008-08-22 15:18:25
W rzeczywistości są sytuacje, w których statment throw
nie zachowa informacji o StackTrace. Na przykład w poniższym kodzie:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
StackTrace będzie wskazywać, że linia 54 podniosła wyjątek, chociaż została podniesiona na linii 47.
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at Program.WithThrowIncomplete() in Program.cs:line 54
at Program.Main(String[] args) in Program.cs:line 106
W sytuacjach takich jak ta opisana powyżej, istnieją dwie opcje, aby zachować oryginalną StackTrace:
Wywołanie wyjątku.InternalPreserveStackTrace
Ponieważ jest to metoda prywatna, to musi być wywołane przez użycie refleksu:
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
Mam wadę polegania na prywatnej metodzie zachowania informacji o StackTrace. Można go zmienić w przyszłych wersjach. NET Framework. Powyższy przykład kodu i proponowane rozwiązanie poniżej zostały zaczerpnięte z Fabrice MARGUERIE weblog .
Wywołanie Wyjątku.SetObjectData
Poniższą technikę zaproponował Anton Tykhyy jako odpowiedź na W C#, Jak mogę InnerException bez utraty stack trace pytanie.
static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;
e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData
// voila, e is unmodified save for _remoteStackTraceString
}
Chociaż ma tę zaletę, że opiera się tylko na metodach publicznych, zależy również od następującego konstruktora WYJĄTKÓW (którego niektóre wyjątki opracowane przez strony trzecie nie implementują):
protected Exception(
SerializationInfo info,
StreamingContext context
)
W mojej sytuacji musiałem wybrać pierwsze podejście, ponieważ wyjątki zgłoszone przez Bibliotekę 3rd-party, z której korzystałem, nie implementowały tego konstruktora.
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-06-01 21:16:41
Kiedy throw ex
, zasadniczo rzucasz nowy wyjątek i stracisz oryginalną informację o śledzeniu stosu. throw
jest preferowaną metodą.
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
2008-08-22 15:15:55
Zasadą jest unikanie łapania i rzucania podstawowego Exception
obiektu. To zmusza cię do bycia nieco mądrzejszym w kwestii WYJĄTKÓW; innymi słowy powinieneś mieć wyraźny haczyk dla SqlException
, aby Twój kod obsługi nie robił czegoś złego z NullReferenceException
.
W prawdziwym świecie jednak, łapanie i logowanie wyjątek podstawowy jest również dobrą praktyką, ale nie zapomnij przejść całość, aby uzyskać InnerExceptions
to może mieć.
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
2008-08-22 15:18:24
Kilka osób przeoczyło bardzo ważny punkt - ' throw 'I' throw ex ' mogą zrobić to samo, ale nie dają Ci kluczowego elementu imformacji, który jest linią, w której wystąpił wyjątek.
Rozważ następujący kod:
static void Main(string[] args)
{
try
{
TestMe();
}
catch (Exception ex)
{
string ss = ex.ToString();
}
}
static void TestMe()
{
try
{
//here's some code that will generate an exception - line #17
}
catch (Exception ex)
{
//throw new ApplicationException(ex.ToString());
throw ex; // line# 22
}
}
Kiedy wykonujesz 'throw' lub 'throw ex', dostajesz ślad stosu, ale linia# będzie #22, więc nie możesz dowiedzieć się, która linia dokładnie rzucała wyjątek (chyba że masz tylko 1 lub kilka linii kodu w próbie blok). Aby uzyskać oczekiwaną linię #17 w swoim wyjątku, musisz rzucić nowy wyjątek z oryginalnym śladem stosu wyjątków.
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-02-08 01:09:37
Powinieneś zawsze używać "throw;", aby zmienić wyjątki w. NET,
Refer this, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
Zasadniczo MSIL (CIL) ma dwie instrukcje - "throw " i"rethrow":
- W przeciwieństwie do poprzednich wersji, nie jest on w pełni funkcjonalny.]}
- C # ' s "throw;" - do MSIL "rethrow"!
Zasadniczo widzę powód, dla którego "throw ex" nadpisuje ślad stosu.
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-08-08 18:26:06
Nikt nie wyjaśnił różnicy między ExceptionDispatchInfo.Capture( ex ).Throw()
a zwykłym throw
, więc oto ona. Jednak niektórzy ludzie zauważyli problem z throw
.
Kompletnym sposobem na zmianę przechwyconego wyjątku jest użycie ExceptionDispatchInfo.Capture( ex ).Throw()
(dostępne tylko z. Net 4.5).
Poniżej znajdują się przypadki niezbędne do przetestowania tego:
1.
void CallingMethod()
{
//try
{
throw new Exception( "TEST" );
}
//catch
{
// throw;
}
}
2.
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
ExceptionDispatchInfo.Capture( ex ).Throw();
throw; // So the compiler doesn't complain about methods which don't either return or throw.
}
}
3.
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch
{
throw;
}
}
4.
void CallingMethod()
{
try
{
throw new Exception( "TEST" );
}
catch( Exception ex )
{
throw new Exception( "RETHROW", ex );
}
}
Przypadek 1 i Przypadek 2 dadzą ci ślad stosu, w którym linia kodu źródłowego number for the CallingMethod
method is the line number of the throw new Exception( "TEST" )
line.
Jednak przypadek 3 daje ślad stosu, w którym numer linii kodu źródłowego metody CallingMethod
jest numerem linii wywołania throw
. Oznacza to, że jeśli linia throw new Exception( "TEST" )
jest otoczona innymi operacjami, nie masz pojęcia, na który numer linii został wyrzucony wyjątek.
Przypadek 4 jest podobny do przypadku 2, ponieważ numer linii oryginalnego wyjątku jest zachowany, ale nie jest prawdziwym rethrowem, ponieważ zmienia typ oryginalnego wyjątku.
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-11-14 11:12:21
Możesz również użyć:
try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}
I wszelkie wyjątki rzucone bąbelki do następnego poziomu, który je obsługuje.
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
2008-08-22 15:32:21
Zdecydowanie użyłbym:
try
{
//some code
}
catch
{
//you should totally do something here, but feel free to rethrow
//if you need to send the exception up the stack.
throw;
}
To zachowa twój stos.
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-04-30 04:25:10
FYI właśnie to przetestowałem i ślad stosu zgłoszony przez 'throw;' nie jest całkowicie poprawnym śladem stosu. Przykład:
private void foo()
{
try
{
bar(3);
bar(2);
bar(1);
bar(0);
}
catch(DivideByZeroException)
{
//log message and rethrow...
throw;
}
}
private void bar(int b)
{
int a = 1;
int c = a/b; // Generate divide by zero exception.
}
Ślad stosu wskazuje prawidłowo początek wyjątku (zgłoszony numer linii), ale numer linii zgłoszony dla foo() jest linią instrukcji throw;, stąd nie można powiedzieć, które z wywołań bar() wywołało 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
2011-04-13 17:37:11