C# Language
Ausnahmebehandlung
Suche…
Grundsätzliche Ausnahmebehandlung
try
{
/* code that could throw an exception */
}
catch (Exception ex)
{
/* handle the exception */
}
Beachten Sie, dass die Behandlung aller Ausnahmen mit demselben Code oft nicht die beste Vorgehensweise ist.
Dies wird im Allgemeinen verwendet, wenn interne Routinen zur Behandlung von Ausnahmen als letzte Möglichkeit ausfallen.
Behandlung bestimmter Ausnahmetypen
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 */
}
Achten Sie darauf, dass Ausnahmen in der Reihenfolge ausgewertet werden und Vererbung angewendet wird. Sie müssen also mit den spezifischsten beginnen und mit ihrem Vorfahren enden. Zu einem bestimmten Zeitpunkt wird nur ein catch-Block ausgeführt.
Verwenden des Ausnahmeobjekts
Sie dürfen Ausnahmen in Ihrem eigenen Code erstellen und werfen. Das Instanziieren einer Ausnahme erfolgt auf dieselbe Weise wie jedes andere C # -Objekt.
Exception ex = new Exception();
// constructor with an overload that takes a message string
Exception ex = new Exception("Error message");
Sie können dann das throw
Schlüsselwort verwenden, um die Ausnahme auszulösen:
try
{
throw new Exception("Error");
}
catch (Exception ex)
{
Console.Write(ex.Message); // Logs 'Error' to the output window
}
Hinweis: Wenn Sie eine neue Ausnahme in einen catch-Block werfen, stellen Sie sicher, dass die ursprüngliche Ausnahme als "innere Ausnahme" übergeben wird, z
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());
}
}
In diesem Fall wird davon ausgegangen, dass die Ausnahme nicht behandelt werden kann, aber der Nachricht einige nützliche Informationen hinzugefügt werden (und auf die ursprüngliche Ausnahme kann weiterhin über ex.InnerException
durch einen äußeren Ausnahmeblock zugegriffen werden).
Es wird etwas zeigen wie:
System.DivideByZeroException: Kann nicht durch b geteilt werden, da es null ist ---> System.DivideByZeroException: Es wurde versucht, durch Null zu teilen.
bei UserQuery.g__DoSomething0_0 () in C: [...] \ LINQPadQuery.cs: Zeile 36
--- Ende der inneren Ausnahmestapelverfolgung ---
bei UserQuery.g__DoSomething0_0 () in C: [...] \ LINQPadQuery.cs: Zeile 42
bei UserQuery.Main () in C: [...] \ LINQPadQuery.cs: Zeile 55
Wenn Sie dieses Beispiel in LinqPad ausprobieren, werden Sie feststellen, dass die Zeilennummern keine große Bedeutung haben (sie helfen Ihnen nicht immer). Das Übergeben eines hilfreichen Fehlertextes, wie oben vorgeschlagen, verringert jedoch häufig die Zeit, um den Ort des Fehlers aufzuspüren, was in diesem Beispiel eindeutig die Zeile ist
c = a / b;
in der Funktion DoSomething()
.
Versuchen Sie es in .NET Fiddle
Zum Schluss blockieren
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 */
}
Der try / catch / finally
Block kann beim Lesen von Dateien sehr praktisch sein.
Zum Beispiel:
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.
}
Auf einen try-Block muss entweder ein catch
oder ein finally
Block folgen. Da es jedoch keinen catch-Block gibt, führt die Ausführung zum Abbruch. Vor der Beendigung werden die Anweisungen innerhalb des finally-Blocks ausgeführt.
Beim Lesen von Dateien hätten wir einen using
Block verwenden können, da FileStream
(was OpenRead
zurückgibt) IDisposable
implementiert.
Selbst wenn im try
Block eine return
Anweisung vorhanden ist, wird der finally
Block normalerweise ausgeführt. Es gibt einige Fälle, in denen dies nicht möglich ist:
- Wenn ein StackOverflow auftritt .
-
Environment.FailFast
- Der Bewerbungsprozess wird in der Regel von einer externen Quelle abgebrochen.
IErrorHandler für WCF-Services implementieren
Die Implementierung von IErrorHandler für WCF-Dienste ist eine hervorragende Möglichkeit, die Fehlerbehandlung und Protokollierung zu zentralisieren. Die hier gezeigte Implementierung sollte alle nicht behandelten Ausnahmen abfangen, die als Ergebnis eines Aufrufs an einen Ihrer WCF-Dienste ausgelöst werden. In diesem Beispiel wird auch gezeigt, wie ein benutzerdefiniertes Objekt zurückgegeben wird und wie JSON anstelle der Standard-XML zurückgegeben wird.
Implementieren Sie 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
In diesem Beispiel hängen wir den Handler an das Serviceverhalten an. Sie könnten dies auch auf ähnliche Weise an IEndpointBehavior, IContractBehavior oder IOperationBehavior anhängen.
An Serviceverhalten anhängen:
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
Configs in 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>
...
Hier sind ein paar Links, die zu diesem Thema hilfreich sein können:
https://msdn.microsoft.com/de-de/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.100).aspx
Andere Beispiele:
IErrorHandler scheint meine Fehler in der WCF nicht zu behandeln. Ideen?
Wie legen Sie den Content-Type-Header für eine HttpClient-Anforderung fest?
Benutzerdefinierte Ausnahmen erstellen
Sie können benutzerdefinierte Ausnahmen implementieren, die wie jede andere Ausnahme ausgelöst werden können. Dies ist sinnvoll, wenn Sie Ihre Ausnahmen während der Laufzeit von anderen Fehlern unterscheiden möchten.
In diesem Beispiel erstellen wir eine benutzerdefinierte Ausnahme, um die Probleme zu beheben, die die Anwendung beim Analysieren einer komplexen Eingabe haben kann.
Angepasste Ausnahmeklasse erstellen
Um eine benutzerdefinierte Ausnahme zu erstellen, erstellen Sie eine Unterklasse von Exception
:
public class ParserException : Exception
{
public ParserException() :
base("The parsing went wrong and we have no additional information.") { }
}
Benutzerdefinierte Ausnahmen werden sehr nützlich, wenn Sie dem Catcher zusätzliche Informationen zur Verfügung stellen möchten:
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;}
}
Wenn Sie nun catch(ParserException x)
Sie über eine zusätzliche Semantik, um die Ausnahmebehandlung zu catch(ParserException x)
.
Benutzerdefinierte Klassen können die folgenden Funktionen implementieren, um zusätzliche Szenarien zu unterstützen.
erneut werfen
Während des Analysevorgangs ist die ursprüngliche Ausnahme immer noch von Interesse. In diesem Beispiel handelt es sich um eine FormatException
da der Code versucht, eine Zeichenfolge zu analysieren, von der erwartet wird, dass sie eine Zahl ist. In diesem Fall sollte die benutzerdefinierte Ausnahme die Aufnahme der ' InnerException ' unterstützen:
//new constructor:
ParserException(string msg, Exception inner) : base(msg, inner) {
}
Serialisierung
In einigen Fällen müssen Ihre Ausnahmen AppDomain-Grenzen überschreiten. Dies ist der Fall, wenn der Parser in einer eigenen AppDomain ausgeführt wird, um das erneute Laden neuer Parserkonfigurationen zu unterstützen. In Visual Studio können Sie eine Exception
Vorlage zum Generieren von Code verwenden.
[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)
{}
}
ParserException verwenden
try
{
Process.StartRun(fileName)
}
catch (ParserException ex)
{
Console.WriteLine($"{ex.Message} in ${ex.FileName}:${ex.LineNumber}");
}
catch (PostProcessException x)
{
...
}
Sie können auch benutzerdefinierte Ausnahmen verwenden, um Ausnahmen abzufangen und zu verpacken. Auf diese Weise können viele verschiedene Fehler in einen einzelnen Fehlertyp umgewandelt werden, der für die Anwendung nützlicher ist:
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);
}
Wenn Sie Ausnahmen behandeln, indem Sie Ihre eigenen benutzerdefinierten Ausnahmen InnerException
, sollten Sie im Allgemeinen einen Verweis auf die ursprüngliche Ausnahme in die InnerException
Eigenschaft InnerException
, wie oben gezeigt.
Sicherheitsbedenken
Wenn der Grund für die Ausnahme angezeigt wird, kann dies die Sicherheit beeinträchtigen, da Benutzer die inneren Abläufe Ihrer Anwendung sehen können. Dies kann eine schlechte Idee sein, die innere Ausnahme zu umgehen. Dies kann zutreffen, wenn Sie eine Klassenbibliothek erstellen, die von anderen verwendet wird.
So können Sie eine benutzerdefinierte Ausnahme auslösen, ohne die innere Ausnahme einzuwickeln:
try
{
// ...
}
catch (SomeStandardException ex)
{
// ...
throw new MyCustomException(someMessage);
}
Fazit
Beim Auslösen einer benutzerdefinierten Ausnahme (entweder beim Wrapping oder bei einer neuen, nicht verpackten Ausnahme) sollten Sie eine für den Aufrufer sinnvolle Ausnahme auslösen. Beispielsweise weiß ein Benutzer einer Klassenbibliothek möglicherweise nicht viel darüber, wie diese Bibliothek ihre interne Arbeit erledigt. Die Ausnahmen, die von den Abhängigkeiten der Klassenbibliothek ausgelöst werden, sind nicht sinnvoll. Der Benutzer wünscht vielmehr eine Ausnahme, die relevant ist, wie die Klassenbibliothek diese Abhängigkeiten fehlerhaft verwendet.
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);
}
Ausnahme Anti-Muster
Ausnahmen schlucken
Eine Ausnahme sollte immer auf folgende Weise wiederholt werden:
try
{
...
}
catch (Exception ex)
{
...
throw;
}
Durch erneutes Auslösen einer Ausnahme wie unten wird die ursprüngliche Ausnahme verschleiert und die ursprüngliche Stapelablaufverfolgung wird verloren gehen. Man sollte das niemals tun! Die Stapelverfolgung vor dem Fangen und Wiederherstellen geht verloren.
try
{
...
}
catch (Exception ex)
{
...
throw ex;
}
Baseball-Ausnahmebehandlung
Ausnahmen sollten nicht als Ersatz für normale Flusssteuerungskonstrukte wie if-then-Anweisungen und while-Schleifen verwendet werden. Dieses Anti-Pattern wird manchmal als Baseball Exception Handling bezeichnet .
Hier ist ein Beispiel für das Anti-Pattern:
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());
}
Hier ist ein besserer Weg, dies zu tun:
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 (Ausnahme)
Es gibt fast keine Gründe (einige sagen: keine!), Um den generischen Ausnahmetyp in Ihrem Code abzufangen. Sie sollten nur die Ausnahmetypen abfangen, die Sie erwarten, da Sie sonst Fehler in Ihrem Code ausblenden.
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?
}
Besser machen:
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");
}
Wenn eine andere Ausnahme auftritt, lassen wir die Anwendung absichtlich abstürzen, sodass sie direkt in den Debugger eintritt und das Problem behoben werden kann. Wir dürfen kein Programm ausliefern, bei dem andere Ausnahmen als diese ohnehin auftreten. Es ist also kein Problem, einen Absturz zu haben.
Das folgende ist auch ein schlechtes Beispiel, da es Ausnahmen verwendet, um Programmierfehler zu umgehen. Dafür sind sie nicht gedacht.
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.
}
Ausnahmen / mehrere Ausnahmen von einer Methode zusammenfassen
Wer sagt, dass Sie nicht mehrere Ausnahmen in einer Methode werfen können. Wenn Sie es nicht gewohnt sind, mit AggregateExceptions herumzuspielen, können Sie versucht sein, Ihre eigene Datenstruktur zu erstellen, um viele Fehlfunktionen darzustellen. Es gibt natürlich eine andere Datenstruktur, bei der es sich nicht um eine Ausnahme handelt, beispielsweise die Ergebnisse einer Validierung. Selbst wenn Sie mit AggregateExceptions spielen, befinden Sie sich möglicherweise auf der Empfängerseite und haben immer damit zu tun, dass sie für Sie nicht von Nutzen sind.
Es ist ziemlich plausibel, eine Methode ausführen zu lassen, und obwohl es insgesamt ein Fehler sein wird, sollten Sie mehrere Dinge hervorheben, die in den ausgelösten Ausnahmen fehlerhaft waren. Ein Beispiel für dieses Verhalten ist, wie parallele Methoden funktionieren, wenn eine Task in mehrere Threads aufgeteilt wurde und eine beliebige Anzahl von ihnen Ausnahmen auslösen könnte, und dies muss gemeldet werden. Hier ein dummes Beispiel, wie Sie davon profitieren können:
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);
}
}
Verschachtelung von Ausnahmen & Try-Catch-Blöcke.
Einer kann eine Ausnahme verschachteln / try
catch
block in den anderen einsetzen.
Auf diese Weise können kleine Codeblöcke verwaltet werden, die funktionieren, ohne dass der gesamte Mechanismus beeinträchtigt wird.
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
}
Hinweis: Vermeiden Sie das Verschlucken von Ausnahmen, wenn Sie in den übergeordneten Fangblock werfen
Best Practices
Cheatsheet
TUN | NICHT |
---|---|
Kontrollfluss mit Kontrollanweisungen | Kontrollieren Sie den Fluss mit Ausnahmen |
Verfolgen Sie ignorierte (absorbierte) Ausnahmen durch Protokollierung | Ausnahme ignorieren |
Wiederholen Sie die Ausnahme mit throw | Ausnahme erneut auslösen - throw new ArgumentNullException() oder throw ex |
Vorgegebene Systemausnahmen auslösen | Eigene Ausnahmen auslösen, die vordefinierten Systemausnahmen ähneln |
Löst eine benutzerdefinierte / vordefinierte Ausnahme aus, wenn dies für die Anwendungslogik entscheidend ist | Werfen Sie benutzerdefinierte / vordefinierte Ausnahmen, um eine Warnung im Fluss anzugeben |
Fangen Sie Ausnahmen auf, die Sie behandeln möchten | Fange jede Ausnahme |
Verwalten Sie die Geschäftslogik NICHT mit Ausnahmen.
Die Flusskontrolle sollte NICHT durch Ausnahmen erfolgen. Verwenden Sie stattdessen bedingte Anweisungen. Wenn eine Kontrolle eindeutig mit der if-else
Anweisung durchgeführt werden kann, dürfen Sie keine Ausnahmen verwenden, da dies die Lesbarkeit und Leistung verringert.
Betrachten Sie den folgenden Ausschnitt von Herrn Bad Practices:
// This is a snippet example for DO NOT
object myObject;
void DoingSomethingWithMyObject()
{
Console.WriteLine(myObject.ToString());
}
Wenn die Ausführung Console.WriteLine(myObject.ToString());
erreicht Console.WriteLine(myObject.ToString());
Die Anwendung löst eine NullReferenceException aus. Mr. Bad Practices erkannte, dass myObject
null ist, und bearbeitete sein Snippet, um die NullReferenceException
myObject
und zu behandeln:
// 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();
}
}
Da das vorherige Snippet nur die Ausnahmelogik behandelt, was soll ich tun, wenn myObject
an dieser Stelle nicht null ist? Wo soll ich diesen Teil der Logik behandeln? Gleich nach Console.WriteLine(myObject.ToString());
? Wie wäre es nach dem try...catch
Block?
Wie wäre es mit Mr. Best Practices? Wie würde er damit umgehen?
// 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();
}
Mr. Best Practices erzielte dieselbe Logik mit weniger Code und einer klaren und verständlichen Logik.
KEINE Ausnahmen erneut auswerfen
Ausnahmen erneut zu werfen ist teuer. Dies wirkt sich negativ auf die Leistung aus. Für Code, der regelmäßig fehlschlägt, können Sie Entwurfsmuster verwenden, um Leistungsprobleme zu minimieren. In diesem Thema werden zwei Entwurfsmuster beschrieben, die hilfreich sind, wenn Ausnahmen die Leistung erheblich beeinträchtigen können.
Nehmen Sie keine Ausnahmen ohne Protokollierung auf
try
{
//Some code that might throw an exception
}
catch(Exception ex)
{
//empty catch block, bad practice
}
Schluck niemals Ausnahmen. Das Ignorieren von Ausnahmen spart diesen Moment, erzeugt jedoch später ein Chaos für die Wartbarkeit. Wenn Sie Ausnahmen protokollieren, sollten Sie immer die Ausnahmeinstanz protokollieren, sodass die gesamte Stack-Ablaufverfolgung protokolliert wird und nicht nur die Ausnahmemeldung.
try
{
//Some code that might throw an exception
}
catch(NullException ex)
{
LogManager.Log(ex.ToString());
}
Fangen Sie keine Ausnahmen auf, mit denen Sie nicht umgehen können
Viele Ressourcen, wie z. B. diese , drängen Sie dringend, zu überlegen, warum Sie an dem Ort, an dem Sie sie abfangen, eine Ausnahme feststellen. Sie sollten eine Ausnahme nur dann abfangen, wenn Sie an dieser Stelle damit umgehen können. Wenn Sie dort etwas tun können, um das Problem zu verringern, beispielsweise einen alternativen Algorithmus ausprobieren, eine Verbindung zu einer Sicherungsdatenbank herstellen, einen anderen Dateinamen versuchen, 30 Sekunden warten und es erneut versuchen oder einen Administrator benachrichtigen, können Sie den Fehler abfangen und das tun. Wenn Sie nichts plausibel und vernünftig tun können, lassen Sie es einfach los und lassen Sie die Ausnahme auf einer höheren Ebene erledigen. Wenn die Ausnahme ausreichend katastrophal ist und es keine vernünftige Option gibt, außer dass das gesamte Programm aufgrund des Schweregrads des Problems abstürzt, lassen Sie es abstürzen.
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.
Unbehandelt und Thread-Ausnahme
AppDomain.UnhandledException Dieses Ereignis benachrichtigt über nicht erfasste Ausnahmen. Es ermöglicht der Anwendung, Informationen über die Ausnahme zu protokollieren, bevor der Standard-Handler des Systems die Ausnahme an den Benutzer meldet und die Anwendung beendet Es können Aktionen ausgeführt werden, z. B. Speichern von Programmdaten für eine spätere Wiederherstellung. Vorsicht wird empfohlen, da Programmdaten beschädigt werden können, wenn keine Ausnahmen behandelt werden.
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledException);
}
Application.ThreadException Dieses Ereignis ermöglicht es Ihrer Windows Forms-Anwendung, ansonsten nicht behandelte Ausnahmen zu behandeln, die in Windows Forms-Threads auftreten. Hängen Sie Ihre Ereignishandler an das ThreadException-Ereignis an, um diese Ausnahmen zu behandeln, wodurch Ihre Anwendung in einem unbekannten Zustand bleibt. Wenn möglich, sollten Ausnahmen von einem strukturierten Ausnahmebehandlungsblock behandelt werden.
/// <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);
}
Und schließlich die Ausnahmebehandlung
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
}
Eine Ausnahme auslösen
Ihr Code kann und sollte häufig eine Ausnahme auslösen, wenn etwas Ungewöhnliches passiert ist.
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.
}