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;
}
Author: Saurav Rastogi, 2008-08-22

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".

 249
Author: Darren Kopp,
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);
}
 93
Author: Mike,
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.

 26
Author: CARLOS LOTH,
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ą.

 18
Author: Forgotten Semicolon,
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ć.

 13
Author: swilliams,
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.

 8
Author: notlkk,
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.

 8
Author: Vinod T. Patil,
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.

 6
Author: jeuoekdcwzfwccu,
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.

 3
Author: Erick 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
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.

 3
Author: 1kevgriff,
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.

 0
Author: redcalx,
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