Szukaj…


Uwagi

Związane z:

Łapanie wyjątku

Kod może i powinien zgłaszać wyjątki w wyjątkowych okolicznościach. Przykłady tego obejmują:

Dzwoniący może obsłużyć te wyjątki, „łapiąc” je i powinien to zrobić tylko wtedy, gdy:

  • Może faktycznie rozwiązać wyjątkowe okoliczności lub odpowiednio wyzdrowieć, lub;
  • Może zapewnić dodatkowy kontekst dla wyjątku, który byłby użyteczny, gdyby wyjątek musiał zostać ponownie zgłoszony (ponownie zgłoszone wyjątki są przechwytywane przez procedury obsługi wyjątków znajdujące się wyżej na stosie wywołań)

Należy zauważyć, że nie wybiera się złapać wyjątek jest całkowicie poprawny, jeśli zamiarem jest, aby była ona obsługiwana na wyższym poziomie.

Złapanie wyjątku odbywa się przez zawinięcie potencjalnie rzucającego się kodu w bloku try { ... } w następujący sposób i przechwycenie wyjątków, które może obsłużyć w bloku catch (ExceptionType) { ... } :

Console.Write("Please enter a filename: ");
string filename = Console.ReadLine();

Stream fileStream;

try
{
    fileStream = File.Open(filename);
}
catch (FileNotFoundException)
{
    Console.WriteLine("File '{0}' could not be found.", filename);
}

Za pomocą wreszcie bloku

Blok w finally { ... } try-finally lub try-catch-finally zawsze będzie wykonywany, niezależnie od tego, czy wystąpił wyjątek, czy nie (z wyjątkiem sytuacji, gdy zgłoszony został wyjątek StackOverflowException lub wywołano funkcję Environment.FailFast() ).

Można go wykorzystać do bezpiecznego uwolnienia lub oczyszczenia zasobów uzyskanych w bloku try { ... } .

Console.Write("Please enter a filename: ");
string filename = Console.ReadLine();

Stream fileStream = null;

try
{
    fileStream = File.Open(filename);
}
catch (FileNotFoundException)
{
    Console.WriteLine("File '{0}' could not be found.", filename);
}
finally
{
    if (fileStream != null)
    {
        fileStream.Dispose();
    }
}

Łapanie i ponowne rzucanie złapane wyjątki

Jeśli chcesz złapać wyjątek i coś zrobić, ale nie możesz kontynuować wykonywania bieżącego bloku kodu z powodu wyjątku, możesz chcieć ponownie wprowadzić wyjątek do następnego modułu obsługi wyjątków na stosie wywołań. Istnieją dobre i złe sposoby na zrobienie tego.

private static void AskTheUltimateQuestion()
{
    try
    {
        var x = 42;
        var y = x / (x - x); // will throw a DivideByZeroException

        // IMPORTANT NOTE: the error in following string format IS intentional
        // and exists to throw an exception to the FormatException catch, below
        Console.WriteLine("The secret to life, the universe, and everything is {1}", y); 
    }
    catch (DivideByZeroException)
    {
        // we do not need a reference to the exception
        Console.WriteLine("Dividing by zero would destroy the universe.");

        // do this to preserve the stack trace:
        throw;
    }
    catch (FormatException ex)
    {
        // only do this if you need to change the type of the Exception to be thrown 
        // and wrap the inner Exception

        // remember that the stack trace of the outer Exception will point to the
        // next line

        // you'll need to examine the InnerException property to get the stack trace
        // to the line that actually started the problem

        throw new InvalidOperationException("Watch your format string indexes.", ex);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Something else horrible happened. The exception: " + ex.Message);

        // do not do this, because the stack trace will be changed to point to
        // this location instead of the location where the exception
        // was originally thrown:
        throw ex; 
    }
}

static void Main()
{
    try
    {
        AskTheUltimateQuestion();
    }
    catch
    {
        // choose this kind of catch if you don't need any information about 
        // the exception that was caught

        // this block "eats" all exceptions instead of rethrowing them
    }
}

Możesz filtrować według typu wyjątku, a nawet według właściwości wyjątku (nowość w C # 6.0, nieco dłużej dostępna w VB.NET (potrzebne cytowanie)):

Dokumentacja / C # / nowe funkcje

Filtry wyjątków

Od wyjątków C # 6.0 można filtrować za pomocą operatora when .

Jest to podobne do używania prostego if ale nie rozwija stosu, jeśli warunek wewnątrz when nie jest spełniony.

Przykład

try
{ 
  // ...
}
catch (Exception e) when (e.InnerException != null) // Any condition can go in here.
{
  // ...
}

Te same informacje można znaleźć w C # 6.0 Funkcje tutaj: Filtry wyjątków

Ponowne sprawdzenie wyjątku w bloku catch

W obrębie bloku catch słowo kluczowe throw może być używane samodzielnie, bez określania wartości wyjątku, w celu ponownego zwrócenia uchwyconego wyjątku. Ponowne wyszukanie wyjątku umożliwia oryginalnemu wyjątkowi kontynuację łańcucha obsługi wyjątków, zachowując stos wywołań lub powiązane dane:

try {...}
catch (Exception ex) {
  // Note: the ex variable is *not* used
  throw;
}

Częstym anty-wzorem jest zamiast tego throw ex , co skutkuje ograniczeniem widoku obsługi wyjątków śledzenia śladu stosu:

try {...}
catch (Exception ex) {
  // Note: the ex variable is thrown
  //  future stack traces of the exception will not see prior calls
  throw ex;  
}

Ogólnie rzecz biorąc, użycie throw ex nie jest pożądane, ponieważ przyszłe programy obsługi wyjątków, które sprawdzą ślad stosu, będą mogły zobaczyć wywołania tylko tak daleko jak throw ex . Pomijając zmienną ex i używając samego słowa kluczowego throw oryginalny wyjątek spowoduje „bąbelkowanie” .

Zgłaszanie wyjątku od innej metody przy zachowaniu jej informacji

Czasami chcesz złapać wyjątek i wyrzucić go z innego wątku lub metody, zachowując oryginalny stos wyjątków. Można to zrobić za pomocą ExceptionDispatchInfo :

using System.Runtime.ExceptionServices;

void Main()
{
    ExceptionDispatchInfo capturedException = null;
    try
    {
        throw new Exception();
    }
    catch (Exception ex)
    {
        capturedException = ExceptionDispatchInfo.Capture(ex);
    }
    
    Foo(capturedException);
}

void Foo(ExceptionDispatchInfo exceptionDispatchInfo)
{
    // Do stuff

    if (capturedException != null)
    {
        // Exception stack trace will show it was thrown from Main() and not from Foo()
        exceptionDispatchInfo.Throw();
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow