Szukaj…


Podstawowa obsługa wyjątków

try
{
    /* code that could throw an exception */
}
catch (Exception ex)
{
    /* handle the exception */
}

Pamiętaj, że obsługa wszystkich wyjątków z tym samym kodem często nie jest najlepszym podejściem.
Jest to powszechnie stosowane, gdy zawiodą jakiekolwiek wewnętrzne procedury obsługi wyjątków, w ostateczności.

Obsługa określonych typów wyjątków

try
{
    /* code to open a file */
}
catch (System.IO.FileNotFoundException)
{
    /* code to handle the file being not found */
}
catch (System.IO.UnauthorizedAccessException)
{
    /* code to handle not being allowed access to the file */
}
catch (System.IO.IOException)
{
    /* code to handle IOException or it's descendant other than the previous two */
}
catch (System.Exception)
{
    /* code to handle other errors */
}

Uważaj, aby wyjątki były oceniane w kolejności i zastosowano dziedziczenie. Musisz więc zacząć od najbardziej specyficznych i zakończyć od ich przodka. W dowolnym momencie zostanie wykonany tylko jeden blok catch.

Korzystanie z obiektu wyjątku

Możesz tworzyć i zgłaszać wyjątki we własnym kodzie. Utworzenie wystąpienia wyjątku odbywa się w taki sam sposób, jak każdy inny obiekt C #.

Exception ex = new Exception();

// constructor with an overload that takes a message string
Exception ex = new Exception("Error message"); 

Następnie możesz użyć słowa kluczowego throw aby zgłosić wyjątek:

try
{
    throw new Exception("Error");
}
catch (Exception ex)
{
    Console.Write(ex.Message); // Logs 'Error' to the output window
} 

Uwaga: Jeśli rzucasz nowy wyjątek w bloku catch, upewnij się, że oryginalny wyjątek jest przekazywany jako „wyjątek wewnętrzny”, np

void DoSomething() 
{
    int b=1; int c=5;
    try
    {
        var a = 1; 
        b = a - 1;
        c = a / b;
        a = a / c;
    }        
    catch (DivideByZeroException dEx) when (b==0)
    {
        // we're throwing the same kind of exception
        throw new DivideByZeroException("Cannot divide by b because it is zero", dEx);
    }
    catch (DivideByZeroException dEx) when (c==0)
    {
        // we're throwing the same kind of exception
        throw new DivideByZeroException("Cannot divide by c because it is zero", dEx);
    }
}

void Main()
{    
    try
    {
        DoSomething();
    }
    catch (Exception ex)
    {
        // Logs full error information (incl. inner exception)
        Console.Write(ex.ToString()); 
    }    
}

W takim przypadku zakłada się, że wyjątek nie może być obsłużony, ale do wiadomości dodawane są pewne przydatne informacje (a do oryginalnego wyjątku nadal można uzyskać dostęp za pośrednictwem np. ex.InnerException przez zewnętrzny blok wyjątków).

Wyświetli się coś takiego:

System.DivideByZeroException: Nie można podzielić przez b, ponieważ wynosi zero ---> System.DivideByZeroException: Próbowano podzielić przez zero.
na UserQuery.g__DoSomething0_0 () w C: [...] \ LINQPadQuery.cs: wiersz 36
--- Koniec stosu wyjątku wewnętrznego śledzenia ---
na UserQuery.g__DoSomething0_0 () w C: [...] \ LINQPadQuery.cs: wiersz 42
w UserQuery.Main () w C: [...] \ LINQPadQuery.cs: wiersz 55

Jeśli wypróbujesz ten przykład w LinqPad, zauważysz, że numery linii nie są bardzo znaczące (nie zawsze ci pomagają). Ale przekazanie pomocnego tekstu błędu, jak sugerowano powyżej, często znacznie skraca czas wyśledzenia lokalizacji błędu, który w tym przykładzie jest wyraźnie linią

c = a / b;

w funkcji DoSomething() .

Wypróbuj w .NET Fiddle

Wreszcie zablokuj

try
{
    /* code that could throw an exception */
}
catch (Exception)
{
    /* handle the exception */
}
finally
{
    /* Code that will be executed, regardless if an exception was thrown / caught or not */
}

Blok try / catch / finally może być bardzo przydatny podczas czytania z plików.
Na przykład:

FileStream f = null;

try
{
    f = File.OpenRead("file.txt");
    /* process the file here */
}
finally
{
    f?.Close(); // f may be null, so use the null conditional operator.
}

Po bloku try musi następować blok catch lub finally . Ponieważ jednak nie ma bloku catch, wykonanie spowoduje zakończenie. Przed zakończeniem zostaną wykonane instrukcje wewnątrz bloku ostatecznie.

Podczas odczytu pliku mogliśmy użyć bloku using , ponieważ FileStream (co zwraca OpenRead ) implementuje IDisposable .

Nawet jeśli w bloku try znajduje się instrukcja return blok w finally zwykle się wykonuje; istnieje kilka przypadków, w których nie będzie:

Implementowanie IErrorHandler dla usług WCF

Wdrożenie IErrorHandler dla usług WCF to świetny sposób na scentralizowanie obsługi błędów i rejestrowania. Przedstawiona tutaj implementacja powinna wychwycić każdy nieobsługiwany wyjątek zgłoszony w wyniku wywołania jednej z usług WCF. W tym przykładzie pokazano również, jak zwrócić obiekt niestandardowy i jak zwrócić JSON zamiast domyślnego XML.

Zaimplementuj IErrorHandler:

using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace BehaviorsAndInspectors
{
    public class ErrorHandler : IErrorHandler
    {

        public bool HandleError(Exception ex)
        {
            // Log exceptions here

            return true;

        } // end

        public void ProvideFault(Exception ex, MessageVersion version, ref Message fault)
        {
            // Get the outgoing response portion of the current context
            var response = WebOperationContext.Current.OutgoingResponse;

            // Set the default http status code 
            response.StatusCode = HttpStatusCode.InternalServerError;

            // Add ContentType header that specifies we are using JSON
            response.ContentType = new MediaTypeHeaderValue("application/json").ToString();

            // Create the fault message that is returned (note the ref parameter) with BaseDataResponseContract                
            fault = Message.CreateMessage(
                version,
                string.Empty,
                new CustomReturnType { ErrorMessage = "An unhandled exception occurred!" },
                new DataContractJsonSerializer(typeof(BaseDataResponseContract), new List<Type> { typeof(BaseDataResponseContract) }));

            if (ex.GetType() == typeof(VariousExceptionTypes))
            {
                 // You might want to catch different types of exceptions here and process them differently
            }

            // Tell WCF to use JSON encoding rather than default XML
            var webBodyFormatMessageProperty = new WebBodyFormatMessageProperty(WebContentFormat.Json);
            fault.Properties.Add(WebBodyFormatMessageProperty.Name, webBodyFormatMessageProperty);

        } // end

    } // end class

} // end namespace

W tym przykładzie dołączamy moduł obsługi do zachowania usługi. Możesz również dołączyć to do IEndpointBehavior, IContractBehavior lub IOperationBehavior w podobny sposób.

Dołącz do zachowań serwisowych:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace BehaviorsAndInspectors
{
    public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
    {
        public override Type BehaviorType
        {
            get { return GetType(); }
        }

        protected override object CreateBehavior()
        {
            return this;
        }

        private IErrorHandler GetInstance()
        {
            return new ErrorHandler();
        }

        void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } // end

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var errorHandlerInstance = GetInstance();

            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(errorHandlerInstance);
            }
        }

        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } // end
      
    } // end class

} // end namespace

Konfiguruje w Web.config:

...
<system.serviceModel>

    <services>      
      <service name="WebServices.MyService">
        <endpoint binding="webHttpBinding" contract="WebServices.IMyService" />
      </service>
    </services>

    <extensions>      
      <behaviorExtensions>        
        <!-- This extension if for the WCF Error Handling-->
        <add name="ErrorHandlerBehavior" type="WebServices.BehaviorsAndInspectors.ErrorHandlerExtensionBehavior, WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />      
      </behaviorExtensions>    
    </extensions>

    <behaviors>          
      <serviceBehaviors>        
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <ErrorHandlerBehavior />
        </behavior>     
      </serviceBehaviors>    
    </behaviors>

    ....
</system.serviceModel>
...

Oto kilka linków, które mogą być pomocne w tym temacie:

https://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.100).aspx

http://www.brainthud.com/cards/5218/25441/which-four-behavior-interfaces-exist-for-interacting-with-a-service-or-client-description-what-methods-do-they- implement-and

Inne przykłady:

IErrorHandler zwraca niepoprawną treść komunikatu, gdy kod stanu HTTP jest 401 Nieautoryzowany

Wygląda na to, że IErrorHandler nie obsługuje moich błędów w WCF ... jakieś pomysły?

Jak sprawić, by niestandardowa procedura obsługi błędów WCF zwracała odpowiedź JSON z niepoprawnym kodem http?

Jak ustawić nagłówek Content-Type dla żądania HttpClient?

Tworzenie niestandardowych wyjątków

Możesz implementować niestandardowe wyjątki, które mogą być zgłaszane tak jak każdy inny wyjątek. Ma to sens, gdy chce się odróżnić wyjątki od innych błędów w czasie wykonywania.

W tym przykładzie utworzymy niestandardowy wyjątek dla jasnego rozwiązywania problemów, które aplikacja może napotkać podczas analizowania złożonych danych wejściowych.

Tworzenie niestandardowej klasy wyjątków

Aby utworzyć niestandardowy wyjątek, utwórz podklasę Exception :

public class ParserException : Exception
{
    public ParserException() : 
      base("The parsing went wrong and we have no additional information.") { }
}

Wyjątek niestandardowy staje się bardzo przydatny, gdy chcesz podać dodatkowe informacje do łapacza:

public class ParserException : Exception
{
    public ParserException(string fileName, int lineNumber) : 
      base($"Parser error in {fileName}:{lineNumber}") 
    {
      FileName = fileName;
      LineNumber = lineNumber;
    }
    public string FileName {get; private set;}
    public int LineNumber {get; private set;}    
}

Teraz, gdy catch(ParserException x) , będziesz miał dodatkową semantykę, aby dostosować obsługę wyjątków.

Klasy niestandardowe mogą implementować następujące funkcje w celu obsługi dodatkowych scenariuszy.

ponowne rzucanie

Podczas procesu analizowania oryginalny wyjątek jest nadal przedmiotem zainteresowania. W tym przykładzie jest to FormatException ponieważ kod próbuje przeanalizować fragment łańcucha, który powinien być liczbą. W takim przypadku wyjątek niestandardowy powinien obsługiwać włączenie „ wyjątku wewnętrznego ”:

//new constructor:
ParserException(string msg, Exception inner) : base(msg, inner) {
}

serializacja

W niektórych przypadkach wyjątki mogą wymagać przekroczenia granic AppDomain. Jest tak w przypadku, gdy analizator składni działa we własnej AppDomain w celu obsługi ponownego ładowania na gorąco nowych konfiguracji analizatora składni. W programie Visual Studio można użyć szablonu Exception do wygenerowania takiego kodu.

[Serializable]
public class ParserException : Exception
{
    // Constructor without arguments allows throwing your exception without
    // providing any information, including error message. Should be included
    // if your exception is meaningful without any additional details. Should
    // set message by calling base constructor (default message is not helpful).
    public ParserException()
        : base("Parser failure.")
    {}

    // Constructor with message argument allows overriding default error message.
    // Should be included if users can provide more helpful messages than
    // generic automatically generated messages.
    public ParserException(string message) 
        : base(message)
    {}

    // Constructor for serialization support. If your exception contains custom
    // properties, read their values here.
    protected ParserException(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    {}
}

Korzystanie z ParserException

try
{
    Process.StartRun(fileName)
}
catch (ParserException ex)
{
    Console.WriteLine($"{ex.Message} in ${ex.FileName}:${ex.LineNumber}");
}
catch (PostProcessException x) 
{
    ...
}

Możesz także użyć niestandardowych wyjątków do wychwytywania i owijania wyjątków. W ten sposób wiele różnych błędów można przekształcić w jeden typ błędu, który jest bardziej przydatny dla aplikacji:

try
{
    int foo = int.Parse(token);
}
catch (FormatException ex)
{
    //Assuming you added this constructor
    throw new ParserException(
      $"Failed to read {token} as number.", 
      FileName, 
      LineNumber, 
      ex);
}

Podczas obsługi wyjątków poprzez zgłaszanie własnych niestandardowych wyjątków należy na ogół zawrzeć odniesienie do oryginalnego wyjątku we właściwości InnerException , jak pokazano powyżej.

Obawy dotyczące bezpieczeństwa

Jeśli ujawnienie przyczyny wyjątku może zagrozić bezpieczeństwu, pozwalając użytkownikom zobaczyć wewnętrzne funkcjonowanie aplikacji, złym pomysłem może być ominięcie wewnętrznego wyjątku. Może to mieć zastosowanie, jeśli tworzysz bibliotekę klas, która będzie używana przez innych.

Oto, w jaki sposób możesz zgłosić niestandardowy wyjątek bez zawijania wewnętrznego wyjątku:

try
{
  // ...
}
catch (SomeStandardException ex)
{
  // ...
  throw new MyCustomException(someMessage);
}

Wniosek

Zgłaszając wyjątek niestandardowy (z opakowaniem lub z nieopakowanym nowym wyjątkiem), należy zgłosić wyjątek, który ma znaczenie dla osoby dzwoniącej. Na przykład użytkownik biblioteki klas może nie wiedzieć wiele o tym, w jaki sposób biblioteka wykonuje swoją wewnętrzną pracę. Wyjątki zgłaszane przez zależności biblioteki klas nie mają znaczenia. Zamiast tego użytkownik chce wyjątku, który jest istotny dla tego, w jaki sposób biblioteka klas używa tych zależności w błędny sposób.

try
{
  // ...
}
catch (IOException ex)
{
  // ...
  throw new StorageServiceException(@"The Storage Service encountered a problem saving
your data. Please consult the inner exception for technical details. 
If you are not able to resolve the problem, please call 555-555-1234 for technical       
assistance.", ex);
}

Wyjątek Anty-wzory

Wyjątki dotyczące połykania

Zawsze należy powtórzyć wyjątek w następujący sposób:

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw;
}

Ponowne zgłoszenie wyjątku, takiego jak poniżej, spowoduje zaciemnienie oryginalnego wyjątku i utraci oryginalny ślad stosu. Nigdy nie należy tego robić! Ślad stosu przed złapaniem i powtórzeniem zostanie utracony.

try
{
    ...
}
catch (Exception ex)
{
    ...
    throw ex;
}

Obsługa wyjątków w baseballu

Nie należy używać wyjątków jako substytutu normalnych konstrukcji kontroli przepływu, takich jak instrukcje if-then i pętle while. Ten anty-wzór jest czasem nazywany obsługą wyjątków baseballowych .

Oto przykład anty-wzoru:

try
{
    while (AccountManager.HasMoreAccounts())
    {
        account = AccountManager.GetNextAccount();
        if (account.Name == userName)
        {
            //We found it
            throw new AccountFoundException(account);
        }
    }
}
catch (AccountFoundException found)
{
    Console.Write("Here are your account details: " + found.Account.Details.ToString());
}

Oto lepszy sposób, aby to zrobić:

Account found = null;
while (AccountManager.HasMoreAccounts() && (found==null))
{
    account = AccountManager.GetNextAccount();
    if (account.Name == userName)
    {
        //We found it
        found = account;
    }
}
Console.Write("Here are your account details: " + found.Details.ToString());

catch (wyjątek)

Prawie nie ma (niektórzy twierdzą, że nie!) Powodów, aby złapać ogólny typ wyjątku w kodzie. Powinieneś wychwycić tylko te typy wyjątków, których się spodziewasz, ponieważ w przeciwnym razie ukryjesz błędy w kodzie.

try 
{
     var f = File.Open(myfile);
     // do something
}
catch (Exception x)
{
     // Assume file not found
     Console.Write("Could not open file");
     // but maybe the error was a NullReferenceException because of a bug in the file handling code?
}

Lepiej zrobić:

try 
{
     var f = File.Open(myfile);
     // do something which should normally not throw exceptions
}
catch (IOException)
{
     Console.Write("File not found");
}
// Unfortunatelly, this one does not derive from the above, so declare separatelly
catch (UnauthorizedAccessException) 
{
     Console.Write("Insufficient rights");
}

Jeśli zdarzy się jakikolwiek inny wyjątek, celowo umożliwiamy awarię aplikacji, więc bezpośrednio wkracza ona do debuggera i możemy rozwiązać problem. Nie wolno nam wysyłać programu, w którym zdarzają się inne wyjątki niż te, więc nie ma problemu z awarią.

Poniższy przykład jest również złym przykładem, ponieważ wykorzystuje on wyjątki do obejścia błędu programowania. Nie do tego są przeznaczone.

public void DoSomething(String s)
{
     if (s == null)
         throw new ArgumentNullException(nameof(s));
     // Implementation goes here
}

try 
{    
     DoSomething(myString);
}
catch(ArgumentNullException x)
{
    // if this happens, we have a programming error and we should check
    // why myString was null in the first place.
}

Zagregowane wyjątki / wiele wyjątków z jednej metody

Kto mówi, że nie możesz zgłosić wielu wyjątków w jednej metodzie. Jeśli nie jesteś przyzwyczajony do zabawy z wyjątkami AggregateException, możesz ulec pokusie, aby stworzyć własną strukturę danych, która reprezentowałaby wiele błędów. Istnieją oczywiście inne struktury danych, które nie są wyjątkiem, byłyby bardziej idealne, takie jak wyniki walidacji. Nawet jeśli grasz z AggregateExceptions, możesz być po stronie przyjmującej i zawsze postępować z nimi, nie zdając sobie sprawy, że mogą ci się przydać.

Wykonywanie metody jest całkiem prawdopodobne i nawet jeśli będzie to niepowodzenie jako całości, będziesz chciał podkreślić wiele rzeczy, które poszły nie tak w zgłoszonych wyjątkach. Jako przykład to zachowanie można zobaczyć na przykładzie działania metod równoległych, które były zadaniem podzielonym na wiele wątków, a dowolna ich liczba mogła zgłaszać wyjątki i należy to zgłosić. Oto głupi przykład tego, jak możesz z tego skorzystać:

    public void Run()
    {
        try
        {
            this.SillyMethod(1, 2);
        }
        catch (AggregateException ex)
        {
            Console.WriteLine(ex.Message);
            foreach (Exception innerException in ex.InnerExceptions)
            {
                Console.WriteLine(innerException.Message);
            }
        }
    }

    private void SillyMethod(int input1, int input2)
    {
        var exceptions = new List<Exception>();

        if (input1 == 1)
        {
            exceptions.Add(new ArgumentException("I do not like ones"));
        }
        if (input2 == 2)
        {
            exceptions.Add(new ArgumentException("I do not like twos"));
        }
        if (exceptions.Any())
        {
            throw new AggregateException("Funny stuff happended during execution", exceptions);
        }
    }

Zagnieżdżanie wyjątków i spróbuj złapać bloki.

Jeden jest w stanie zagnieździć jeden wyjątek / try catch blok w drugim.

W ten sposób można zarządzać małymi blokami kodu, które są w stanie działać bez zakłócania całego mechanizmu.

try 
{
//some code here
    try 
    {
        //some thing which throws an exception. For Eg : divide by 0
    }
    catch (DivideByZeroException dzEx)
    {
        //handle here only this exception
        //throw from here will be passed on to the parent catch block
    }
    finally
    {
        //any thing to do after it is done.
    }
 //resume from here & proceed as normal; 
}
catch(Exception e)
{
    //handle here
}

Uwaga: Unikaj wyjątków dotyczących połykania podczas rzucania do nadrzędnego bloku catch

Najlepsze praktyki

Ściągawka

ROBIĆ NIE
Przepływ sterowania za pomocą instrukcji sterujących Kontrola przepływu z wyjątkami
Śledź zignorowany (wchłonięty) wyjątek, logując się Zignoruj wyjątek
Powtórz wyjątek, używając throw Re-throw wyjątek - throw new ArgumentNullException() lub throw ex
Zgłaszaj predefiniowane wyjątki systemowe Zgłaszaj niestandardowe wyjątki podobne do predefiniowanych wyjątków systemowych
Zgłaszaj niestandardowy / predefiniowany wyjątek, jeśli ma to zasadnicze znaczenie dla logiki aplikacji Rzuć niestandardowe / predefiniowane wyjątki, aby podać ostrzeżenie
Złap wyjątki, które chcesz obsłużyć Złap każdy wyjątek

NIE zarządzaj logiką biznesową z wyjątkami.

Kontrola przepływu NIE powinna być wykonywana w drodze wyjątków. Zamiast tego użyj instrukcji warunkowych. Jeśli kontrola może być wykonana za if-else instrukcji if-else wyraźnie, nie używaj wyjątków, ponieważ zmniejsza to czytelność i wydajność.

Rozważ następujący fragment autorstwa Bad Bad Practices:

// This is a snippet example for DO NOT
object myObject;
void DoingSomethingWithMyObject()
{
    Console.WriteLine(myObject.ToString());
}

Kiedy wykonanie osiągnie Console.WriteLine(myObject.ToString()); aplikacja zgłosi wyjątek NullReferenceException. Pan Bad Practices zdał sobie sprawę, że myObject ma wartość NULL i zmodyfikował swój fragment kodu, aby przechwycić NullReferenceException i obsłużyć go:

// This is a snippet example for DO NOT
object myObject;
void DoingSomethingWithMyObject()
{
    try
    {
        Console.WriteLine(myObject.ToString());
    }
    catch(NullReferenceException ex)
    {
        // Hmmm, if I create a new instance of object and assign it to myObject:
        myObject = new object();
        // Nice, now I can continue to work with myObject
        DoSomethingElseWithMyObject();
    }
}

Ponieważ poprzedni fragment obejmuje jedynie logikę wyjątku, co należy zrobić, jeśli myObject nie jest null w tym momencie? Gdzie powinienem omówić tę część logiki? Zaraz po Console.WriteLine(myObject.ToString()); ? Co powiesz na blok try...catch ?

Co powiesz na Mr. Best Practices? Jak sobie z tym poradzi?

// This is a snippet example for DO
object myObject;
void DoingSomethingWithMyObject()
{
    if(myObject == null)
        myObject = new object();
    
    // When execution reaches this point, we are sure that myObject is not null
    DoSomethingElseWithMyObject();
}

Pan Best Practices osiągnął tę samą logikę przy mniejszej liczbie kodów oraz jasnej i zrozumiałej logice.

NIE rzucaj wyjątkami

Ponowne zgłaszanie wyjątków jest drogie. Wpływa to negatywnie na wydajność. W przypadku kodu, który rutynowo zawodzi, można użyć wzorców projektowych, aby zminimalizować problemy z wydajnością. W tym temacie opisano dwa wzorce projektowe, które są przydatne, gdy wyjątki mogą znacząco wpłynąć na wydajność.

NIE wchłaniaj wyjątków bez rejestrowania

try
{
    //Some code that might throw an exception
}
catch(Exception ex)
{
    //empty catch block, bad practice
}

Nigdy nie połykaj wyjątków. Ignorowanie wyjątków uratuje ten moment, ale spowoduje chaos w celu późniejszego utrzymania. Podczas rejestrowania wyjątków należy zawsze rejestrować wystąpienie wyjątku, aby rejestrowane było pełne śledzenie stosu, a nie tylko komunikat wyjątku.

try
{
    //Some code that might throw an exception
}
catch(NullException ex)
{
    LogManager.Log(ex.ToString());
}

Nie wychwytuj wyjątków, których nie możesz obsłużyć

Wiele zasobów, takich jak ten , zdecydowanie zachęca do zastanowienia się, dlaczego łapiesz wyjątek w miejscu, w którym go łapiesz. Powinieneś wychwycić wyjątek tylko wtedy, gdy możesz go obsłużyć w tym miejscu. Jeśli możesz coś zrobić, aby złagodzić problem, na przykład wypróbować alternatywny algorytm, połączyć się z zapasową bazą danych, wypróbować inną nazwę pliku, poczekać 30 sekund i spróbować ponownie lub powiadomić administratora, możesz złapać błąd i zrobić to. Jeśli nie ma nic, co można zrobić w wiarygodny i racjonalny sposób, po prostu „puść to” i pozwól, aby wyjątek został obsłużony na wyższym poziomie. Jeśli wyjątek jest wystarczająco katastrofalny i nie ma innej rozsądnej opcji niż zawieszenie całego programu z powodu powagi problemu, pozwól mu ulec awarii.

try
{
    //Try to save the data to the main database.
}
catch(SqlException ex)
{
    //Try to save the data to the alternative database.
}
//If anything other than a SqlException is thrown, there is nothing we can do here. Let the exception bubble up to a level where it can be handled.

Nieobsługiwany i wyjątek wątku

AppDomain.UnhandledException To zdarzenie zapewnia powiadomienie o nieprzechwyconych wyjątkach. Pozwala aplikacji rejestrować informacje o wyjątku, zanim systemowa domyślna procedura obsługi zgłosi wyjątek użytkownikowi i zakończy działanie aplikacji. Jeśli dostępne są wystarczające informacje o stanie aplikacji, inne mogą zostać podjęte działania - takie jak zapisanie danych programu do późniejszego odzyskania. Zaleca się ostrożność, ponieważ dane programu mogą ulec uszkodzeniu, gdy wyjątki nie będą obsługiwane.

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);            
    }

Application.ThreadException To zdarzenie pozwala aplikacji Windows Forms na obsługę nieobsługiwanych wyjątków występujących w wątkach Windows Forms. Dołącz moduły obsługi zdarzeń do zdarzenia ThreadException, aby poradzić sobie z tymi wyjątkami, co spowoduje pozostawienie aplikacji w nieznanym stanie. Tam, gdzie to możliwe, wyjątki powinny być obsługiwane przez ustrukturyzowany blok obsługi wyjątków.

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);
        Application.ThreadException += new ThreadExceptionEventHandler(ThreadException);
    }

I wreszcie obsługa wyjątków

static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = (Exception)e.ExceptionObject;
        // your code
    }

static void ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        Exception ex = e.Exception;
        // your code
    }

Zgłaszanie wyjątku

Twój kod może i często powinien generować wyjątek, gdy wydarzyło się coś niezwykłego.

public void WalkInto(Destination destination)
{
    if (destination.Name == "Mordor")
    {
        throw new InvalidOperationException("One does not simply walk into Mordor.");
    }
    // ... Implement your normal walking code here.
}


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