Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De juiste afhandeling van uitzonderingen is essentieel voor de betrouwbaarheid van toepassingen. U kunt opzettelijk verwachte uitzonderingen afhandelen om te voorkomen dat uw app vastloopt. Een vastgelopen app is echter betrouwbaarder en diagnosticeerbaar dan een app met niet-gedefinieerd gedrag.
In dit artikel worden aanbevolen procedures beschreven voor het afhandelen en maken van uitzonderingen.
Uitzonderingen verwerken
De volgende aanbevolen procedures zijn van belang voor het afhandelen van uitzonderingen:
- Gebruik try/catch/finally-blokken om te herstellen van fouten of om bronnen vrij te geven
- algemene voorwaarden afhandelen om uitzonderingen te voorkomen
- Catch-annulering en asynchrone uitzonderingen
- Ontwerpklassen, zodat uitzonderingen kunnen worden vermeden
- herstelstatus wanneer methoden niet worden voltooid vanwege uitzonderingen
- Uitzonderingen correct opvangen en opnieuw gooien
Try/catch/finally blocks gebruiken om te herstellen van fouten of om bronnen vrij te geven.
Gebruik try/catch blokken rond de code voor code die mogelijk een uitzondering kan genereren en wanneer uw app deze uitzondering kan herstellen. In catch blokken kunt u altijd uitzonderingen van de meest afgeleide naar de minst afgeleide volgorde sorteren. (Alle uitzonderingen zijn afgeleid van de klasse Exception. Meer afgeleide uitzonderingen worden niet verwerkt door een catch-component die wordt voorafgegaan door een catch-component voor een basisuitzonderingsklasse.) Wanneer uw code niet kan worden hersteld na een uitzondering, kunt u deze uitzondering niet ondervangen. Sta methoden hoger in de aanroepstack toe om, indien mogelijk, te herstellen.
Maak de resources schoon die zijn toegewezen met using-instructies of finally-blokken. Geef de voorkeur aan using instructies om resources automatisch op te ruimen wanneer er uitzonderingen worden opgeworpen. Gebruik finally blokken om bronnen op te ruimen die IDisposableniet implementeren. Code in een finally-component wordt bijna altijd uitgevoerd, zelfs wanneer er uitzonderingen worden gegenereerd.
Algemene voorwaarden afhandelen om uitzonderingen te voorkomen
Voor voorwaarden die waarschijnlijk optreden, maar een uitzondering kan veroorzaken, kunt u overwegen deze op een manier te verwerken die de uitzondering vermijdt. Als u bijvoorbeeld probeert een verbinding te sluiten die al is gesloten, krijgt u een InvalidOperationException. U kunt dit voorkomen door een if instructie te gebruiken om de verbindingsstatus te controleren voordat u deze probeert te sluiten.
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
If conn.State <> ConnectionState.Closed Then
conn.Close()
End IF
Als u de verbindingsstatus niet controleert voordat u afsluit, kunt u de InvalidOperationException uitzondering ondervangen.
try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}
Try
conn.Close()
Catch ex As InvalidOperationException
Console.WriteLine(ex.GetType().FullName)
Console.WriteLine(ex.Message)
End Try
De gekozen methode is afhankelijk van hoe vaak u verwacht dat de gebeurtenis plaatsvindt.
Gebruik uitzonderingsafhandeling als de gebeurtenis niet vaak voorkomt, dat wil gezegd, als de gebeurtenis echt uitzonderlijk is en een fout aangeeft, zoals een onverwacht einde van het bestand. Wanneer u uitzonderingsafhandeling gebruikt, wordt minder code uitgevoerd in normale omstandigheden.
Controleer op foutcodes in code als de gebeurtenis regelmatig plaatsvindt en kan worden beschouwd als onderdeel van de normale uitvoering. Wanneer u controleert op veelvoorkomende foutvoorwaarden, wordt minder code uitgevoerd omdat u uitzonderingen vermijdt.
Notitie
Controles vooraf elimineren meestal uitzonderingen. Er kunnen echter racevoorwaarden zijn waarbij de beveiligde voorwaarde verandert tussen de controle en de bewerking, en in dat geval kunt u nog steeds een uitzondering maken.
Methoden Try* aanroepen om uitzonderingen te voorkomen
Als de prestatienadelen van uitzonderingen te hoog zijn, bieden sommige methoden van de .NET-bibliotheek alternatieve vormen van foutafhandeling.
Int32.Parse genereert bijvoorbeeld een OverflowException als de te parseren waarde te groot is om te worden weergegeven door Int32.
Int32.TryParse genereert deze uitzondering echter niet. In plaats daarvan retourneert het een Booleaanse waarde en heeft een out parameter die het geparseerde geldige gehele getal bevat bij succes.
Dictionary<TKey,TValue>.TryGetValue heeft vergelijkbaar gedrag voor het ophalen van een waarde uit een woordenlijst.
Annulerings- en asynchrone uitzonderingen vangen
Het is beter om OperationCanceledException te vangen in plaats van TaskCanceledException, die is afgeleid van OperationCanceledException, wanneer u een asynchrone methode aanroept. Veel asynchrone methoden genereren een OperationCanceledException uitzondering als annulering wordt aangevraagd. Met deze uitzonderingen kan de uitvoering efficiënt worden gestopt en wordt de callstack niet meer uitgevoerd zodra er een annuleringsaanvraag wordt waargenomen.
Asynchrone methoden slaan uitzonderingen op die worden gegenereerd tijdens de uitvoering in de taak die ze retourneren. Als er een uitzondering wordt opgeslagen in de geretourneerde taak, wordt die uitzondering opgeworpen wanneer de taak wordt afgewacht. Gebruiksonderzondering, zoals ArgumentException, worden nog steeds synchroon gegenereerd. Zie Asynchrone uitzonderingenvoor meer informatie.
Ontwerpklassen zodat uitzonderingen kunnen worden vermeden
Een klasse kan methoden of eigenschappen bieden waarmee u kunt voorkomen dat u een aanroep maakt die een uitzondering activeert. De FileStream-klasse biedt bijvoorbeeld methoden waarmee u kunt bepalen of het einde van het bestand is bereikt. U kunt deze methoden aanroepen om de uitzondering te voorkomen die wordt gegenereerd als u voorbij het einde van het bestand leest. In het volgende voorbeeld ziet u hoe u naar het einde van een bestand kunt lezen zonder een uitzondering te activeren:
class FileRead
{
public static void ReadAll(FileStream fileToRead)
{
ArgumentNullException.ThrowIfNull(fileToRead);
int b;
// Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin);
// Read each byte to the end of the file.
for (int i = 0; i < fileToRead.Length; i++)
{
b = fileToRead.ReadByte();
Console.Write(b.ToString());
// Or do something else with the byte.
}
}
}
Class FileRead
Public Sub ReadAll(fileToRead As FileStream)
' This if statement is optional
' as it is very unlikely that
' the stream would ever be null.
If fileToRead Is Nothing Then
Throw New System.ArgumentNullException()
End If
Dim b As Integer
' Set the stream position to the beginning of the file.
fileToRead.Seek(0, SeekOrigin.Begin)
' Read each byte to the end of the file.
For i As Integer = 0 To fileToRead.Length - 1
b = fileToRead.ReadByte()
Console.Write(b.ToString())
' Or do something else with the byte.
Next i
End Sub
End Class
Een andere manier om uitzonderingen te voorkomen, is om null (of standaard) te retourneren voor de meest voorkomende foutgevallen in plaats van een uitzondering te genereren. Een veelvoorkomende foutcase kan worden beschouwd als een normale controlestroom. Als u in deze gevallen null (of standaard) retourneert, minimaliseert u de gevolgen voor de prestaties van een app.
Voor waardetypen kunt u overwegen of u Nullable<T> of default als foutindicator voor uw app wilt gebruiken. Door Nullable<Guid>te gebruiken, wordt defaultnull in plaats van Guid.Empty. Soms kan het toevoegen van Nullable<T> het duidelijker maken wanneer een waarde aanwezig of afwezig is. Andere keren kan het toevoegen van Nullable<T> extra gevallen toevoegen om te controleren die niet nodig zijn en alleen dienen om potentiële foutenbronnen te creëren.
Herstelstatus wanneer methoden niet worden voltooid vanwege uitzonderingen
Aanroepers moeten kunnen aannemen dat er geen bijwerkingen zijn wanneer een uitzondering wordt gegooid vanuit een methode. Als u bijvoorbeeld code hebt die geld overdraagt door geld op te nemen van de ene rekening en te storten op een andere rekening, en er een fout optreedt tijdens de verwerking van de storting, wilt u niet dat de opname van kracht blijft.
public void TransferFunds(Account from, Account to, decimal amount)
{
from.Withdrawal(amount);
// If the deposit fails, the withdrawal shouldn't remain in effect.
to.Deposit(amount);
}
Public Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
from.Withdrawal(amount)
' If the deposit fails, the withdrawal shouldn't remain in effect.
[to].Deposit(amount)
End Sub
Met de voorgaande methode worden geen uitzonderingen rechtstreeks gegooid. U moet echter de methode schrijven zodat de intrekking wordt omgekeerd als de stortingsbewerking mislukt.
Een manier om deze situatie te verwerken, is door eventuele uitzonderingen te ondervangen die door de stortingstransactie worden gegenereerd en de intrekking ongedaan te maken.
private static void TransferFunds(Account from, Account to, decimal amount)
{
string withdrawalTrxID = from.Withdrawal(amount);
try
{
to.Deposit(amount);
}
catch
{
from.RollbackTransaction(withdrawalTrxID);
throw;
}
}
Private Shared Sub TransferFunds(from As Account, [to] As Account, amount As Decimal)
Dim withdrawalTrxID As String = from.Withdrawal(amount)
Try
[to].Deposit(amount)
Catch
from.RollbackTransaction(withdrawalTrxID)
Throw
End Try
End Sub
In dit voorbeeld ziet u het gebruik van throw om de oorspronkelijke uitzondering opnieuw te werpen, waardoor bellers gemakkelijker de echte oorzaak van het probleem kunnen zien zonder de eigenschap InnerException te hoeven onderzoeken. Een alternatief is om een nieuwe uitzondering te genereren en de oorspronkelijke uitzondering op te nemen als de interne uitzondering.
catch (Exception ex)
{
from.RollbackTransaction(withdrawalTrxID);
throw new TransferFundsException("Withdrawal failed.", innerException: ex)
{
From = from,
To = to,
Amount = amount
};
}
Catch ex As Exception
from.RollbackTransaction(withdrawalTrxID)
Throw New TransferFundsException("Withdrawal failed.", innerException:=ex) With
{
.From = from,
.[To] = [to],
.Amount = amount
}
End Try
Uitzonderingen goed vastleggen en opnieuw werpen
Zodra er een uitzondering wordt gegooid, maakt de stacktrace deel uit van de meegevoerde informatie. De stack-trace is een lijst van de hiërarchie van methode-aanroepen die begint met de methode die de uitzondering gooit en eindigt met de methode die de uitzondering vangt. Als u een uitzondering opnieuw uitvoert door de uitzondering op te geven in de throw-instructie, bijvoorbeeld throw e, wordt de stack-trace opnieuw gestart bij de huidige methode en wordt de lijst met methodeaanroepen tussen de oorspronkelijke methode die de uitzondering heeft veroorzaakt en de huidige methode verloren gegaan. Als u de oorspronkelijke stack-trace informatie bij de uitzondering wilt behouden, zijn er twee opties die afhankelijk zijn van waar u de uitzondering opnieuw wilt gooien.
- Als u de uitzondering vanuit de handler (
catchblok) die de uitzonderingsexemplaren heeft gedetecteerd, opnieuw uitvoert, gebruikt u dethrow-instructie zonder de uitzondering op te geven. Codeanalyseregel CA2200 helpt u bij het vinden van plaatsen in uw code waar u per ongeluk stacktraceringsgegevens kwijtraakt. - Als u de uitzondering opnieuw gooit vanaf een andere locatie dan de handler (
catchblok), gebruikt u ExceptionDispatchInfo.Capture(Exception) om de uitzondering in de handler vast te leggen en ExceptionDispatchInfo.Throw() wanneer u deze opnieuw wilt gooien. U kunt de eigenschap ExceptionDispatchInfo.SourceException gebruiken om de vastgelegde uitzondering te controleren.
In het volgende voorbeeld ziet u hoe de ExceptionDispatchInfo klasse kan worden gebruikt en hoe de uitvoer eruit kan zien.
ExceptionDispatchInfo? edi = null;
try
{
var txt = File.ReadAllText(@"C:\temp\file.txt");
}
catch (FileNotFoundException e)
{
edi = ExceptionDispatchInfo.Capture(e);
}
// ...
Console.WriteLine("I was here.");
if (edi is not null)
edi.Throw();
Als het bestand in de voorbeeldcode niet bestaat, wordt de volgende uitvoer geproduceerd:
I was here.
Unhandled exception. System.IO.FileNotFoundException: Could not find file 'C:\temp\file.txt'.
File name: 'C:\temp\file.txt'
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding encoding, Int32 bufferSize)
at System.IO.File.ReadAllText(String path, Encoding encoding)
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 12
--- End of stack trace from previous location ---
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 24
Excepties gooien
De volgende aanbevolen procedures hebben betrekking op de wijze waarop u uitzonderingen genereert:
- Vooraf gedefinieerde uitzonderingstypen gebruiken
- Methoden voor het maken van uitzonderingen gebruiken
- een gelokaliseerd tekenreeksbericht opnemen
- De juiste grammatica gebruiken
- Plaats throw-instructies goed
- maak geen uitzonderingen in definitieve componenten
- Geen uitzonderingen genereren op onverwachte plaatsen
- validatie-uitzonderingen voor argumenten synchroon
Vooraf gedefinieerde uitzonderingstypen gebruiken
Introduceer alleen een nieuwe uitzonderingsklasse wanneer een vooraf gedefinieerde klasse niet van toepassing is. Bijvoorbeeld:
- Als een eigenschapsset of methodeaanroep niet geschikt is gezien de huidige status van het object, genereert u een InvalidOperationException uitzondering.
- Als er ongeldige parameters worden doorgegeven, genereert u een ArgumentException uitzondering of een van de vooraf gedefinieerde klassen die zijn afgeleid van ArgumentException.
Notitie
Hoewel het raadzaam is om indien mogelijk vooraf gedefinieerde uitzonderingstypen te gebruiken, moet u niet enkele gereserveerde uitzonderingstypen genereren, zoals AccessViolationException, IndexOutOfRangeException, NullReferenceException en StackOverflowException. Zie CA2201 voor meer informatie: Geen gereserveerde uitzonderingstypen genereren.
Methoden voor het maken van uitzonderingen gebruiken
Het is gebruikelijk dat een klasse dezelfde uitzondering genereert op verschillende plaatsen in de implementatie. Als u overmatige code wilt voorkomen, maakt u een helpermethode waarmee de uitzondering wordt gemaakt en deze wordt geretourneerd. Bijvoorbeeld:
class FileReader
{
private readonly string _fileName;
public FileReader(string path)
{
_fileName = path;
}
public byte[] Read(int bytes)
{
byte[] results = FileUtils.ReadFromFile(_fileName, bytes) ?? throw NewFileIOException();
return results;
}
static FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";
return new FileReaderException(description);
}
}
Class FileReader
Private fileName As String
Public Sub New(path As String)
fileName = path
End Sub
Public Function Read(bytes As Integer) As Byte()
Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
If results Is Nothing
Throw NewFileIOException()
End If
Return results
End Function
Function NewFileIOException() As FileReaderException
Dim description As String = "My NewFileIOException Description"
Return New FileReaderException(description)
End Function
End Class
Sommige belangrijke .NET-uitzonderingstypen hebben dergelijke statische throw helpermethoden die de uitzondering toewijzen en genereren. U moet deze methoden aanroepen in plaats van het bijbehorende uitzonderingstype te maken en op te geven:
- ArgumentNullException.ThrowIfNull
- ArgumentException.ThrowIfNullOrEmpty(String, String)
- ArgumentException.ThrowIfNullOrWhiteSpace(String, String)
- ArgumentOutOfRangeException.ThrowIfZero<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfNegative<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfLessThan<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfNotEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfNegativeOrZero<T>(T, String)
- ArgumentOutOfRangeException.ThrowIfGreaterThan<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual<T>(T, T, String)
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual<T>(T, T, String)
- ObjectDisposedException.ThrowIf
Aanbeveling
Met de volgende regels voor codeanalyse kunt u plaatsen in uw code vinden waar u kunt profiteren van deze statische throw helpers: CA1510, CA1511, CA1512en CA1513.
Als u een asynchrone methode implementeert, roept u CancellationToken.ThrowIfCancellationRequested() aan in plaats van te controleren of annulering is aangevraagd en vervolgens OperationCanceledExceptionte maken en te genereren. Zie CA2250voor meer informatie.
Een gelokaliseerd tekenreeksbericht opnemen
Het foutbericht dat de gebruiker ziet, is afgeleid van de eigenschap Exception.Message van de uitzondering die is gegenereerd en niet van de naam van de uitzonderingsklasse. Normaal gesproken wijst u een waarde toe aan de eigenschap Exception.Message door de berichttekenreeks door te geven aan het message argument van een Uitzonderingsconstructor.
Voor gelokaliseerde toepassingen moet u een gelokaliseerde berichttekenreeks opgeven voor elke uitzondering die uw toepassing kan genereren. U gebruikt resourcebestanden om gelokaliseerde foutberichten op te geven. Zie de volgende artikelen voor informatie over het lokaliseren van toepassingen en het ophalen van gelokaliseerde tekenreeksen:
- Procedure: Door de gebruiker gedefinieerde uitzonderingen maken met gelokaliseerde uitzonderingsberichten
- Resources in .NET-apps
- System.Resources.ResourceManager
De juiste grammatica gebruiken
Schrijf duidelijke zinnen en voeg leestekens aan het einde toe. Elke zin in de tekenreeks die is toegewezen aan de eigenschap Exception.Message moet in een punt eindigen. Bijvoorbeeld: 'De logboektabel is overgelopen'. Gebruikt de juiste grammatica en leestekens.
Zet throw-uitdrukkingen op de juiste plaats.
Plaats throw-verklaringen waar de stacktrace nuttig is. De stack-trace begint bij de instructie waarin de uitzondering optreedt en eindigt bij de catch instructie waarmee de uitzondering wordt vastgelegd.
Maak geen uitzonderingen in ten slotte clausules
Verhef geen uitzonderingen in finally-clausules. Zie codeanalyseregel CA2219voor meer informatie.
Geen uitzonderingen van onverwachte plaatsen genereren
Sommige methoden, zoals Equals, GetHashCodeen ToString methoden, statische constructors en gelijkheidsoperators, mogen geen uitzonderingen genereren. Zie codeanalyseregel CA1065-voor meer informatie.
Validatie-uitzonderingen voor argumenten synchroon genereren
In methoden voor het retourneren van taken moet u argumenten valideren en eventuele bijbehorende uitzonderingen genereren, zoals ArgumentException en ArgumentNullException, voordat u het asynchrone deel van de methode invoert. Uitzonderingen die worden gegenereerd in het asynchrone deel van de methode, worden opgeslagen in de geretourneerde taak en komen pas naar voren wanneer, bijvoorbeeld, de taak wordt afgewacht. Zie Uitzonderingen in methoden voor het retourneren van takenvoor meer informatie.
Aangepaste uitzonderingstypen
De volgende aanbevolen procedures hebben betrekking op aangepaste uitzonderingstypen:
- namen van uitzonderingsklassen beëindigen met
Exception - Drie constructors opnemen
- Geef indien nodig meer eigenschappen op
Namen van uitzonderingsklassen beëindigen met Exception
Wanneer een aangepaste uitzondering nodig is, moet u deze op de juiste wijze een naam opgeven en afleiden uit de Exception-klasse. Bijvoorbeeld:
public class MyFileNotFoundException : Exception
{
}
Public Class MyFileNotFoundException
Inherits Exception
End Class
Drie constructors opnemen
Gebruik ten minste de drie algemene constructors bij het maken van uw eigen uitzonderingsklassen: de parameterloze constructor, een constructor die een tekenreeksbericht gebruikt en een constructor die een tekenreeksbericht en een interne uitzondering accepteert.
- Exception(), waarvoor standaardwaarden worden gebruikt.
- Exception(String), waarmee een tekenreeksbericht wordt geaccepteerd.
- Exception(String, Exception), die een tekenreeksbericht en een interne uitzondering accepteert.
Zie voor een voorbeeld Procedure: Door de gebruiker gedefinieerde uitzonderingen maken.
Geef indien nodig meer eigenschappen op
Geef meer eigenschappen op voor een uitzondering (naast de aangepaste berichttekenreeks) alleen als er een programmatisch scenario is waarin de aanvullende informatie nuttig is. De FileNotFoundException biedt bijvoorbeeld de eigenschap FileName.