Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.
Klassen Exception är basklassen för alla undantag. När ett fel inträffar rapporterar antingen systemet eller det program som körs för närvarande det genom att utlösa ett undantag som innehåller information om felet. När ett undantag har genererats hanteras det av programmet eller av standardfelhanteraren.
Fel och undantag
Körningsfel kan inträffa av olika anledningar. Alla fel bör dock inte hanteras som undantag i koden. Här är några kategorier av fel som kan inträffa vid körning och lämpliga sätt att svara på dem.
Användningsfel. Ett användningsfel representerar ett fel i programlogik som kan resultera i ett undantag. Felet bör dock åtgärdas inte genom undantagshantering utan genom att ändra den felaktiga koden. Åsidosättningen Object.Equals(Object) av metoden i följande exempel förutsätter till exempel att
objargumentet alltid måste vara icke-null.using System; public class Person1 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation contains an error in program logic: // It assumes that the obj argument is not null. Person1 p = (Person1) obj; return this.Name.Equals(p.Name); } } public class UsageErrorsEx1 { public static void Main() { Person1 p1 = new Person1(); p1.Name = "John"; Person1 p2 = null; // The following throws a NullReferenceException. Console.WriteLine($"p1 = p2: {p1.Equals(p2)}"); } }// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation contains an error in program logic: // It assumes that the obj argument is not null. let p = obj :?> Person this.Name.Equals p.Name let p1 = Person() p1.Name <- "John" let p2: Person = null // The following throws a NullReferenceException. printfn $"p1 = p2: {p1.Equals p2}"Public Class Person Private _name As String Public Property Name As String Get Return _name End Get Set _name = value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation contains an error in program logic: ' It assumes that the obj argument is not null. Dim p As Person = CType(obj, Person) Return Me.Name.Equals(p.Name) End Function End Class Module Example2 Public Sub Main() Dim p1 As New Person() p1.Name = "John" Dim p2 As Person = Nothing ' The following throws a NullReferenceException. Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End ModuleUndantaget NullReferenceException som resulterar när
objärnullkan elimineras genom att ändra källkoden för att explicit testa för null innan åsidosättningen Object.Equals anropas och sedan kompileras igen. Följande exempel innehåller den korrigerade källkoden som hanterar ettnullargument.using System; public class Person2 { private string _name; public string Name { get { return _name; } set { _name = value; } } public override int GetHashCode() { return this.Name.GetHashCode(); } public override bool Equals(object obj) { // This implementation handles a null obj argument. Person2 p = obj as Person2; if (p == null) return false; else return this.Name.Equals(p.Name); } } public class UsageErrorsEx2 { public static void Main() { Person2 p1 = new Person2(); p1.Name = "John"; Person2 p2 = null; Console.WriteLine($"p1 = p2: {p1.Equals(p2)}"); } } // The example displays the following output: // p1 = p2: False// In F#, null is not a valid state for declared types // without 'AllowNullLiteralAttribute' [<AllowNullLiteral>] type Person() = member val Name = "" with get, set override this.GetHashCode() = this.Name.GetHashCode() override this.Equals(obj) = // This implementation handles a null obj argument. match obj with | :? Person as p -> this.Name.Equals p.Name | _ -> false let p1 = Person() p1.Name <- "John" let p2: Person = null printfn $"p1 = p2: {p1.Equals p2}" // The example displays the following output: // p1 = p2: FalsePublic Class Person2 Private _name As String Public Property Name As String Get Return _name End Get Set _name = Value End Set End Property Public Overrides Function Equals(obj As Object) As Boolean ' This implementation handles a null obj argument. Dim p As Person2 = TryCast(obj, Person2) If p Is Nothing Then Return False Else Return Me.Name.Equals(p.Name) End If End Function End Class Module Example3 Public Sub Main() Dim p1 As New Person2() p1.Name = "John" Dim p2 As Person2 = Nothing Console.WriteLine("p1 = p2: {0}", p1.Equals(p2)) End Sub End Module ' The example displays the following output: ' p1 = p2: FalseI stället för att använda undantagshantering för användningsfel kan du använda Debug.Assert metoden för att identifiera användningsfel i felsökningsversioner och Trace.Assert metoden för att identifiera användningsfel i både felsöknings- och versionsversioner. Mer information finns i Kontroller i Hanterad kod.
Programfel. Ett programfel är ett körningsfel som inte nödvändigtvis kan undvikas genom att skriva felfri kod.
I vissa fall kan ett programfel återspegla ett förväntat eller rutinmässigt feltillstånd. I det här fallet kanske du vill undvika att använda undantagshantering för att hantera programfelet och i stället försöka utföra åtgärden igen. Om användaren till exempel förväntas ange ett datum i ett visst format kan du parsa datumsträngen genom att anropa DateTime.TryParseExact metoden, som returnerar ett Boolean värde som anger om parsningsåtgärden lyckades, i stället för att använda DateTime.ParseExact metoden, vilket utlöser ett FormatException undantag om datumsträngen inte kan konverteras till ett DateTime värde. På samma sätt, om en användare försöker öppna en fil som inte finns, kan du först anropa File.Exists metoden för att kontrollera om filen finns och, om den inte gör det, fråga användaren om de vill skapa den.
I andra fall återspeglar ett programfel ett oväntat feltillstånd som kan hanteras i koden. Även om du till exempel har kontrollerat att en fil finns kan den tas bort innan du kan öppna den, eller så kan den vara skadad. I så fall kan försök att öppna filen genom att instansiera ett StreamReader objekt eller anropa Open metoden utlösa ett FileNotFoundException undantag. I dessa fall bör du använda undantagshantering för att återställa från felet.
Systemfel. Ett systemfel är ett körningsfel som inte kan hanteras programmatiskt på ett meningsfullt sätt. Vilken metod som helst kan till exempel utlösa ett OutOfMemoryException undantag om den vanliga språkkörningen inte kan allokera ytterligare minne. Normalt hanteras inte systemfel med hjälp av undantagshantering. I stället kanske du kan använda en händelse som AppDomain.UnhandledException och anropa Environment.FailFast metoden för att logga undantagsinformation och meddela användaren om felet innan programmet avslutas.
Prova/fånga block
Den vanliga språkkörningen tillhandahåller en undantagshanteringsmodell som baseras på representationen av undantag som objekt, och separationen av programkod och undantagshanteringskod i try block och catch block. Det kan finnas ett eller flera catch block, var och en utformad för att hantera en viss typ av undantag, eller ett block som är utformat för att fånga ett mer specifikt undantag än ett annat block.
Om ett program hanterar undantag som inträffar under körningen av ett block med programkod måste koden placeras i en try -instruktion och kallas för ett try block. Programkod som hanterar undantag som genereras av ett try block placeras i en catch -instruktion och kallas för ett catch block. Noll eller fler catch block är associerade med ett try block, och varje catch block innehåller ett typfilter som avgör vilka typer av undantag det hanterar.
När ett undantag inträffar i ett try block söker systemet igenom de associerade catch blocken i den ordning de visas i programkoden tills det hittar ett catch block som hanterar undantaget. Ett catch block hanterar ett undantag av typen T om typfiltret för catch-blocket anger T eller någon typ som T härleds från. Systemet slutar söka efter det första catch blocket som hanterar undantaget. Därför måste ett catch block som hanterar en typ anges i programkod före ett catch block som hanterar dess bastyper, vilket visas i exemplet som följer i det här avsnittet. Ett catch-block som hanterar System.Exception anges sist.
Om inget av blocken catch som är associerade med det aktuella try blocket hanterar undantaget och det aktuella try blocket är kapslat i andra try block i det aktuella anropet genomsöks de catch block som är associerade med nästa omslutande try block. Om inget catch block för undantaget hittas söker systemet efter tidigare kapslingsnivåer i det aktuella anropet. Om inget catch block för undantaget hittas i det aktuella anropet skickas undantaget upp i anropsstacken och den tidigare stackramen söks efter ett catch block som hanterar undantaget. Sökningen av anropsstacken fortsätter tills undantaget hanteras eller tills det inte finns fler bildrutor i anropsstacken. Om toppen av anropsstacken nås utan att hitta ett catch block som hanterar undantaget hanterar standardfelhanteraren det och programmet avslutas.
F# try.. med uttryck
F# använder inte catch block. I stället matchas ett upphöjt undantag med ett enda with block. Eftersom det här är ett uttryck i stället för en sats måste alla sökvägar returnera samma typ. Mer information finns i Prova... med uttryck.
Funktioner för undantagstyper
Undantagstyper stöder följande funktioner:
Läsbar text som beskriver felet. När ett undantag inträffar gör körmiljön ett textmeddelande tillgängligt för att informera användaren om felets art och föreslå åtgärder för att lösa problemet. Det här textmeddelandet lagras i Message egenskapen för undantagsobjektet. När du skapar undantagsobjektet kan du skicka en textsträng till konstruktorn för att beskriva information om det specifika undantaget. Om inget felmeddelande har angetts för konstruktorn används standardfelmeddelandet. För mer information, se egenskapen Message.
Tillståndet för anropsstacken när undantaget utlöstes. Egenskapen StackTrace har en stackspårning som kan användas för att avgöra var felet inträffar i koden. Stackspårningen visar alla anropade metoder och radnummer i källfilen där anropen görs.
Egenskaper för undantagsklass
Klassen Exception innehåller ett antal egenskaper som hjälper dig att identifiera kodplatsen, typen, hjälpfilen och orsaken till undantaget: StackTrace, InnerException, Message, HelpLink, HResult, Source, TargetSiteoch Data.
När det finns ett orsakssamband mellan två eller flera undantag behåller egenskapen denna InnerException information. Det yttre undantaget genereras som svar på det här inre undantaget. Koden som hanterar det yttre undantaget kan använda informationen från det tidigare inre undantaget för att hantera felet på ett mer lämpligt sätt. Ytterligare information om undantaget kan lagras som en samling nyckel/värde-par i egenskapen Data .
Felmeddelandesträngen som skickas till konstruktorn när undantagsobjektet skapas ska lokaliseras och kan anges från en resursfil med hjälp ResourceManager av klassen . Mer information om lokaliserade resurser finns i avsnittet Skapa satellitsammansättningar och paketering och distribution av resurser .
För att ge användaren omfattande information om varför undantaget inträffade HelpLink kan egenskapen innehålla en URL (eller URN) till en hjälpfil.
Klassen Exception använder HRESULT COR_E_EXCEPTION, som har värdet 0x80131500.
En lista över inledande egenskapsvärden för en instans av Exception klassen finns i Exception konstruktorerna.
Prestandaöverväganden
Att utlösa eller hantera ett undantag förbrukar en betydande mängd systemresurser och körningstid. Generera undantag endast för att hantera verkligt extraordinära förhållanden, inte för att hantera förutsägbara händelser eller flödeskontroll. I vissa fall, till exempel när du utvecklar ett klassbibliotek, är det till exempel rimligt att utlösa ett undantag om ett metodargument är ogiltigt, eftersom du förväntar dig att din metod anropas med giltiga parametrar. Ett ogiltigt metodargument, om det inte är resultatet av ett användningsfel, innebär att något extraordinärt har inträffat. Omvänt bör du inte utlösa ett undantag om användarindata är ogiltiga, eftersom du kan förvänta dig att användare ibland anger ogiltiga data. Ange i stället en mekanism för återförsök så att användarna kan ange giltiga indata. Du bör inte heller använda undantag för att hantera användningsfel. Använd i stället påståenden för att identifiera och korrigera användningsfel.
Dessutom ska du inte utlösa ett undantag när en returkod är tillräcklig. konvertera inte en returkod till ett undantag. och fånga inte rutinmässigt ett undantag, ignorera det och fortsätt sedan bearbetningen.
Kasta om ett undantag
I många fall vill en undantagshanterare bara skicka undantaget vidare till anroparen. Detta inträffar oftast i:
Ett klassbibliotek som i sin tur omsluter anrop till metoder i .NET-klassbiblioteket eller andra klassbibliotek.
Ett program eller bibliotek som stöter på ett allvarligt undantag. Undantagshanteraren kan logga undantaget och sedan generera undantaget igen.
Det rekommenderade sättet att återskapa ett undantag är att helt enkelt använda instruktionen throw i C#, funktionen reraise i F# och throw-instruktionen i Visual Basic utan att inkludera ett uttryck. Detta säkerställer att all information om anropsstacken bevaras när undantaget sprids till anroparen. I följande exempel visas detta. En strängtilläggsmetod, FindOccurrences, omsluter ett eller flera anrop till String.IndexOf(String, Int32) utan att verifiera argumenten i förväg.
using System;
using System.Collections.Generic;
public static class Library1
{
public static int[] FindOccurrences(this String s, String f)
{
var indexes = new List<int>();
int currentIndex = 0;
try
{
while (currentIndex >= 0 && currentIndex < s.Length)
{
currentIndex = s.IndexOf(f, currentIndex);
if (currentIndex >= 0)
{
indexes.Add(currentIndex);
currentIndex++;
}
}
}
catch (ArgumentNullException)
{
// Perform some action here, such as logging this exception.
throw;
}
return indexes.ToArray();
}
}
open System
module Library =
let findOccurrences (s: string) (f: string) =
let indexes = ResizeArray()
let mutable currentIndex = 0
try
while currentIndex >= 0 && currentIndex < s.Length do
currentIndex <- s.IndexOf(f, currentIndex)
if currentIndex >= 0 then
indexes.Add currentIndex
currentIndex <- currentIndex + 1
with :? ArgumentNullException ->
// Perform some action here, such as logging this exception.
reraise ()
indexes.ToArray()
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices
Public Module Library
<Extension()>
Public Function FindOccurrences1(s As String, f As String) As Integer()
Dim indexes As New List(Of Integer)
Dim currentIndex As Integer = 0
Try
Do While currentIndex >= 0 And currentIndex < s.Length
currentIndex = s.IndexOf(f, currentIndex)
If currentIndex >= 0 Then
indexes.Add(currentIndex)
currentIndex += 1
End If
Loop
Catch e As ArgumentNullException
' Perform some action here, such as logging this exception.
Throw
End Try
Return indexes.ToArray()
End Function
End Module
En person ringer sedan FindOccurrences två gånger. I det andra anropet till FindOccurrences skickar anroparen en null som söksträng, vilket får metoden String.IndexOf(String, Int32) att utlösa ett ArgumentNullException undantag. Det här undantaget hanteras av FindOccurrences metoden och skickas tillbaka till anroparen. Eftersom throw-instruktionen används utan uttryck visar utdata från exemplet att anropsstacken bevaras.
public class RethrowEx1
{
public static void Main()
{
String s = "It was a cold day when...";
int[] indexes = s.FindOccurrences("a");
ShowOccurrences(s, "a", indexes);
Console.WriteLine();
String toFind = null;
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine($"An exception ({e.GetType().Name}) occurred.");
Console.WriteLine($"Message:{Environment.NewLine} {e.Message}{Environment.NewLine}");
Console.WriteLine($"Stack Trace:{Environment.NewLine} {e.StackTrace}{Environment.NewLine}");
}
}
private static void ShowOccurrences(String s, String toFind, int[] indexes)
{
Console.Write("'{0}' occurs at the following character positions: ",
toFind);
for (int ctr = 0; ctr < indexes.Length; ctr++)
Console.Write("{0}{1}", indexes[ctr],
ctr == indexes.Length - 1 ? "" : ", ");
Console.WriteLine();
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
open Library
let showOccurrences toFind (indexes: int[]) =
printf $"'{toFind}' occurs at the following character positions: "
for i = 0 to indexes.Length - 1 do
printf $"""{indexes[i]}{if i = indexes.Length - 1 then "" else ", "}"""
printfn ""
let s = "It was a cold day when..."
let indexes = findOccurrences s "a"
showOccurrences "a" indexes
printfn ""
let toFind: string = null
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $"Message:\n {e.Message}\n"
printfn $"Stack Trace:\n {e.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null. (Parameter 'value')
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.findOccurrences(String s, String f)
// at <StartupCode$fs>.main@()
Module Example1
Public Sub Main()
Dim s As String = "It was a cold day when..."
Dim indexes() As Integer = s.FindOccurrences1("a")
ShowOccurrences(s, "a", indexes)
Console.WriteLine()
Dim toFind As String = Nothing
Try
indexes = s.FindOccurrences1(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine("Message:{0} {1}{0}", vbCrLf, e.Message)
Console.WriteLine("Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
End Try
End Sub
Private Sub ShowOccurrences(s As String, toFind As String, indexes As Integer())
Console.Write("'{0}' occurs at the following character positions: ",
toFind)
For ctr As Integer = 0 To indexes.Length - 1
Console.Write("{0}{1}", indexes(ctr),
If(ctr = indexes.Length - 1, "", ", "))
Next
Console.WriteLine()
End Sub
End Module
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message:
' Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
Om undantaget istället kastas igen med hjälp av den här satsen:
throw e;
Throw e
raise e
... sedan bevaras inte den fullständiga anropsstacken och exemplet genererar följande utdata:
'a' occurs at the following character positions: 4, 7, 15
An exception (ArgumentNullException) occurred.
Message:
Value cannot be null.
Parameter name: value
Stack Trace:
at Library.FindOccurrences(String s, String f)
at Example.Main()
Ett något mer besvärligt alternativ är att kasta ett nytt undantag och bevara det ursprungliga undantagets anropsstack i ett inre undantag. Anroparen kan sedan använda det nya undantagets InnerException egenskap för att hämta stackram och annan information om det ursprungliga undantaget. I det här fallet är utkastssatsen:
throw new ArgumentNullException("You must supply a search string.", e);
raise (ArgumentNullException("You must supply a search string.", e) )
Throw New ArgumentNullException("You must supply a search string.",
e)
Användarkoden som hanterar undantaget måste veta att InnerException egenskapen innehåller information om det ursprungliga undantaget, vilket visas i följande undantagshanterare.
try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine($"An exception ({e.GetType().Name}) occurred.");
Console.WriteLine($" Message:{Environment.NewLine}{e.Message}");
Console.WriteLine($" Stack Trace:{Environment.NewLine} {e.StackTrace}");
Exception ie = e.InnerException;
if (ie != null)
{
Console.WriteLine(" The Inner Exception:");
Console.WriteLine($" Exception Name: {ie.GetType().Name}");
Console.WriteLine($" Message: {ie.Message}{Environment.NewLine}");
Console.WriteLine($" Stack Trace:{Environment.NewLine} {ie.StackTrace}{Environment.NewLine}");
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
try
let indexes = findOccurrences s toFind
showOccurrences toFind indexes
with :? ArgumentNullException as e ->
printfn $"An exception ({e.GetType().Name}) occurred."
printfn $" Message:\n{e.Message}"
printfn $" Stack Trace:\n {e.StackTrace}"
let ie = e.InnerException
if ie <> null then
printfn " The Inner Exception:"
printfn $" Exception Name: {ie.GetType().Name}"
printfn $" Message: {ie.Message}\n"
printfn $" Stack Trace:\n {ie.StackTrace}\n"
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
Try
indexes = s.FindOccurrences(toFind)
ShowOccurrences(s, toFind, indexes)
Catch e As ArgumentNullException
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, e.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, e.StackTrace)
Dim ie As Exception = e.InnerException
If ie IsNot Nothing Then
Console.WriteLine(" The Inner Exception:")
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name)
Console.WriteLine(" Message: {1}{0}", vbCrLf, ie.Message)
Console.WriteLine(" Stack Trace:{0} {1}{0}", vbCrLf, ie.StackTrace)
End If
End Try
' The example displays the following output:
' 'a' occurs at the following character positions: 4, 7, 15
'
' An exception (ArgumentNullException) occurred.
' Message: You must supply a search string.
'
' Stack Trace:
' at Library.FindOccurrences(String s, String f)
' at Example.Main()
'
' The Inner Exception:
' Exception Name: ArgumentNullException
' Message: Value cannot be null.
' Parameter name: value
'
' Stack Trace:
' at System.String.IndexOf(String value, Int32 startIndex, Int32 count, Stri
' ngComparison comparisonType)
' at Library.FindOccurrences(String s, String f)
Välj standardundantag
När du måste utlösa ett undantag kan du ofta använda en befintlig undantagstyp i .NET i stället för att implementera ett anpassat undantag. Du bör använda en standard undantagstyp under dessa två villkor:
Du utlöser ett undantag som orsakas av ett användningsfel (det vill: ett fel i programlogik som skapats av utvecklaren som anropar din metod). Vanligtvis utlöser du ett undantag som ArgumentException, ArgumentNullException, InvalidOperationExceptioneller NotSupportedException. Strängen som du anger till undantagsobjektets konstruktor när du instansierar undantagsobjektet bör beskriva felet så att utvecklaren kan åtgärda det. För mer information, se egenskapen Message.
Du hanterar ett fel som kan kommuniceras med anroparen med ett befintligt .NET-undantag. Du bör kasta det mest härledda undantaget möjligt. Om en metod till exempel kräver att ett argument är en giltig medlem av en uppräkningstyp bör du utlösa en InvalidEnumArgumentException (den mest härledda klassen) i stället för en ArgumentException.
I följande tabell visas vanliga undantagstyper och de villkor under vilka du skulle kasta dem.
| Undantag | Tillstånd |
|---|---|
| ArgumentException | Ett icke-null-argument som skickas till en metod är ogiltigt. |
| ArgumentNullException | Ett argument som skickas till en metod är null. |
| ArgumentOutOfRangeException | Ett argument ligger utanför intervallet med giltiga värden. |
| DirectoryNotFoundException | En del av en katalogsökväg är inte giltig. |
| DivideByZeroException | Nämnaren i ett heltal eller Decimal en division är noll. |
| DriveNotFoundException | En disk är otillgänglig eller existerar inte. |
| FileNotFoundException | Det finns ingen fil. |
| FormatException | Ett värde är inte i ett lämpligt format som ska konverteras från en sträng av en konverteringsmetod, till exempel Parse. |
| IndexOutOfRangeException | Ett index ligger utanför gränserna för en matris eller samling. |
| InvalidOperationException | Ett metodanrop är ogiltigt i ett objekts aktuella tillstånd. |
| KeyNotFoundException | Det går inte att hitta den angivna nyckeln för åtkomst till en medlem i en samling. |
| NotImplementedException | En metod eller åtgärd implementeras inte. |
| NotSupportedException | En metod eller åtgärd stöds inte. |
| ObjectDisposedException | En åtgärd utförs på ett objekt som har tagits bort. |
| OverflowException | En aritmetik-, gjutnings- eller konverteringsåtgärd resulterar i ett spill. |
| PathTooLongException | En sökväg eller filnamn överskrider den maximala systemdefinierade längden. |
| PlatformNotSupportedException | Åtgärden stöds inte på den aktuella plattformen. |
| RankException | En matris med fel antal dimensioner skickas till en metod. |
| TimeoutException | Tidsintervallet som tilldelats en åtgärd har upphört att gälla. |
| UriFormatException | En ogiltig URI (Uniform Resource Identifier) används. |
Implementera anpassade undantag
I följande fall är det inte tillräckligt att använda ett befintligt .NET-undantag för att hantera ett felvillkor:
När undantaget visar ett unikt programfel som inte kan mappas till ett befintligt .NET-undantag.
När undantaget kräver hantering som skiljer sig från den hantering som är lämplig för ett befintligt .NET-undantag, eller om undantaget måste skiljas från ett liknande undantag. Om du till exempel utlöser ett ArgumentOutOfRangeException undantag när du parsar den numeriska representationen av en sträng som ligger inom intervallet för målintegreringstypen, vill du inte använda samma undantag för ett fel som uppstår när anroparen inte anger lämpliga begränsade värden när metoden anropas.
Klassen Exception är basklassen för alla undantag i .NET. Många härledda klasser förlitar sig på det ärvda beteendet hos medlemmarna i Exception klassen. De åsidosätter inte medlemmarna Exceptioni och definierar inte heller några unika medlemmar.
Så här definierar du din egen undantagsklass:
Definiera en klass som ärver från Exception. Om det behövs definierar du eventuella unika medlemmar som krävs av din klass för att ge ytterligare information om undantaget. Klassen innehåller till exempel ArgumentException en ParamName egenskap som anger namnet på parametern vars argument orsakade undantaget, och RegexMatchTimeoutException egenskapen innehåller en MatchTimeout egenskap som anger tidsgränsintervallet.
Om det behövs, åsidosätt alla ärvda medlemmar vars funktionalitet du vill ändra eller modifiera. Observera att de flesta befintliga härledda klasserna Exception inte åsidosätter beteendet för ärvda medlemmar.
Kontrollera om ditt anpassade undantagsobjekt är serialiserbart. Med serialisering kan du spara information om undantaget och tillåta att undantagsinformation delas av en server och en klientproxy i en fjärrkommunikationskontext. Om du vill göra undantagsobjektet serialiserbart markerar du det med attributet SerializableAttribute .
Definiera konstruktorerna för undantagsklassen. Undantagsklasser har vanligtvis en eller flera av följande konstruktorer:
Exception(), som använder standardvärden för att initiera egenskaperna för ett nytt undantagsobjekt.
Exception(String), som initierar ett nytt undantagsobjekt med ett angivet felmeddelande.
Exception(String, Exception), som initierar ett nytt undantagsobjekt med ett angivet felmeddelande och ett inre undantag.
Exception(SerializationInfo, StreamingContext), som är en
protectedkonstruktor som initierar ett nytt undantagsobjekt från serialiserade data. Du bör implementera den här konstruktorn om du har valt att göra undantagsobjektet serialiserbart.
I följande exempel visas användningen av en anpassad undantagsklass. Det definierar ett NotPrimeException undantag som utlöses när en klient försöker hämta en sekvens med primtal genom att ange ett startnummer som inte är primärt. Undantaget definierar en ny egenskap, NonPrime, som returnerar det icke-primära tal som orsakade undantaget. Förutom att implementera en skyddad parameterlös konstruktor och en konstruktor med SerializationInfo och StreamingContext parametrar för serialisering NotPrimeException definierar klassen ytterligare tre konstruktorer som stöder NonPrime egenskapen. Varje konstruktor anropar en basklasskonstruktor förutom att bevara värdet för det icke-primära talet. Klassen NotPrimeException är också markerad med attributet SerializableAttribute .
using System;
using System.Runtime.Serialization;
[Serializable()]
public class NotPrimeException : Exception
{
private int notAPrime;
protected NotPrimeException()
: base()
{ }
public NotPrimeException(int value) :
base(String.Format("{0} is not a prime number.", value))
{
notAPrime = value;
}
public NotPrimeException(int value, string message)
: base(message)
{
notAPrime = value;
}
public NotPrimeException(int value, string message, Exception innerException) :
base(message, innerException)
{
notAPrime = value;
}
protected NotPrimeException(SerializationInfo info,
StreamingContext context)
: base(info, context)
{ }
public int NonPrime
{ get { return notAPrime; } }
}
namespace global
open System
open System.Runtime.Serialization
[<Serializable>]
type NotPrimeException =
inherit Exception
val notAPrime: int
member this.NonPrime =
this.notAPrime
new (value) =
{ inherit Exception($"%i{value} is not a prime number."); notAPrime = value }
new (value, message) =
{ inherit Exception(message); notAPrime = value }
new (value, message, innerException: Exception) =
{ inherit Exception(message, innerException); notAPrime = value }
// F# does not support protected members
new () =
{ inherit Exception(); notAPrime = 0 }
new (info: SerializationInfo, context: StreamingContext) =
{ inherit Exception(info, context); notAPrime = 0 }
Imports System.Runtime.Serialization
<Serializable()> _
Public Class NotPrimeException : Inherits Exception
Private notAPrime As Integer
Protected Sub New()
MyBase.New()
End Sub
Public Sub New(value As Integer)
MyBase.New(String.Format("{0} is not a prime number.", value))
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String)
MyBase.New(message)
notAPrime = value
End Sub
Public Sub New(value As Integer, message As String, innerException As Exception)
MyBase.New(message, innerException)
notAPrime = value
End Sub
Protected Sub New(info As SerializationInfo,
context As StreamingContext)
MyBase.New(info, context)
End Sub
Public ReadOnly Property NonPrime As Integer
Get
Return notAPrime
End Get
End Property
End Class
Klassen PrimeNumberGenerator som visas i följande exempel använder Sieve of Eratosthenes för att beräkna sekvensen med primtal från 2 till en gräns som anges av klienten i anropet till dess klasskonstruktor. Metoden GetPrimesFrom returnerar alla primtal som är större än eller lika med en angiven lägre gräns, men genererar en NotPrimeException om den lägre gränsen inte är ett primärt tal.
using System;
using System.Collections.Generic;
[Serializable]
public class PrimeNumberGenerator
{
private const int START = 2;
private int maxUpperBound = 10000000;
private int upperBound;
private bool[] primeTable;
private List<int> primes = new List<int>();
public PrimeNumberGenerator(int upperBound)
{
if (upperBound > maxUpperBound)
{
string message = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound);
throw new ArgumentOutOfRangeException(message);
}
this.upperBound = upperBound;
// Create array and mark 0, 1 as not prime (True).
primeTable = new bool[upperBound + 1];
primeTable[0] = true;
primeTable[1] = true;
// Use Sieve of Eratosthenes to determine prime numbers.
for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound));
ctr++)
{
if (primeTable[ctr]) continue;
for (int multiplier = ctr; multiplier <= upperBound / ctr; multiplier++)
if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier] = true;
}
// Populate array with prime number information.
int index = START;
while (index != -1)
{
index = Array.FindIndex(primeTable, index, (flag) => !flag);
if (index >= 1)
{
primes.Add(index);
index++;
}
}
}
public int[] GetAllPrimes()
{
return primes.ToArray();
}
public int[] GetPrimesFrom(int prime)
{
int start = primes.FindIndex((value) => value == prime);
if (start < 0)
throw new NotPrimeException(prime, String.Format("{0} is not a prime number.", prime));
else
return primes.FindAll((value) => value >= prime).ToArray();
}
}
namespace global
open System
[<Serializable>]
type PrimeNumberGenerator(upperBound) =
let start = 2
let maxUpperBound = 10000000
let primes = ResizeArray()
let primeTable =
upperBound + 1
|> Array.zeroCreate<bool>
do
if upperBound > maxUpperBound then
let message = $"{upperBound} exceeds the maximum upper bound of {maxUpperBound}."
raise (ArgumentOutOfRangeException message)
// Create array and mark 0, 1 as not prime (True).
primeTable[0] <- true
primeTable[1] <- true
// Use Sieve of Eratosthenes to determine prime numbers.
for i = start to float upperBound |> sqrt |> ceil |> int do
if not primeTable[i] then
for multiplier = i to upperBound / i do
if i * multiplier <= upperBound then
primeTable[i * multiplier] <- true
// Populate array with prime number information.
let mutable index = start
while index <> -1 do
index <- Array.FindIndex(primeTable, index, fun flag -> not flag)
if index >= 1 then
primes.Add index
index <- index + 1
member _.GetAllPrimes() =
primes.ToArray()
member _.GetPrimesFrom(prime) =
let start =
Seq.findIndex ((=) prime) primes
if start < 0 then
raise (NotPrimeException(prime, $"{prime} is not a prime number.") )
else
Seq.filter ((>=) prime) primes
|> Seq.toArray
Imports System.Collections.Generic
<Serializable()> Public Class PrimeNumberGenerator
Private Const START As Integer = 2
Private maxUpperBound As Integer = 10000000
Private upperBound As Integer
Private primeTable() As Boolean
Private primes As New List(Of Integer)
Public Sub New(upperBound As Integer)
If upperBound > maxUpperBound Then
Dim message As String = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound)
Throw New ArgumentOutOfRangeException(message)
End If
Me.upperBound = upperBound
' Create array and mark 0, 1 as not prime (True).
ReDim primeTable(upperBound)
primeTable(0) = True
primeTable(1) = True
' Use Sieve of Eratosthenes to determine prime numbers.
For ctr As Integer = START To CInt(Math.Ceiling(Math.Sqrt(upperBound)))
If primeTable(ctr) Then Continue For
For multiplier As Integer = ctr To CInt(upperBound \ ctr)
If ctr * multiplier <= upperBound Then primeTable(ctr * multiplier) = True
Next
Next
' Populate array with prime number information.
Dim index As Integer = START
Do While index <> -1
index = Array.FindIndex(primeTable, index, Function(flag)
Return Not flag
End Function)
If index >= 1 Then
primes.Add(index)
index += 1
End If
Loop
End Sub
Public Function GetAllPrimes() As Integer()
Return primes.ToArray()
End Function
Public Function GetPrimesFrom(prime As Integer) As Integer()
Dim start As Integer = primes.FindIndex(Function(value)
Return value = prime
End Function)
If start < 0 Then
Throw New NotPrimeException(prime, String.Format("{0} is not a prime number.", prime))
Else
Return primes.FindAll(Function(value)
Return value >= prime
End Function).ToArray()
End If
End Function
End Class
I följande exempel görs två anrop till GetPrimesFrom metoden med icke-primära tal, varav ett överskrider gränserna för programdomäner. I båda fallen kastas undantaget och hanteras framgångsrikt i klientkoden.
using System;
using System.Reflection;
class Example1
{
public static void Main()
{
int limit = 10000000;
PrimeNumberGenerator primes = new PrimeNumberGenerator(limit);
int start = 1000001;
try
{
int[] values = primes.GetPrimesFrom(start);
Console.WriteLine($"There are {start} prime numbers from {limit} to {2}");
}
catch (NotPrimeException e)
{
Console.WriteLine($"{e.NonPrime} is not prime");
Console.WriteLine(e);
Console.WriteLine("--------");
}
AppDomain domain = AppDomain.CreateDomain("Domain2");
PrimeNumberGenerator gen = (PrimeNumberGenerator)domain.CreateInstanceAndUnwrap(
typeof(Example).Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
new object[] { 1000000 }, null, null);
try
{
start = 100;
Console.WriteLine(gen.GetPrimesFrom(start));
}
catch (NotPrimeException e)
{
Console.WriteLine($"{e.NonPrime} is not prime");
Console.WriteLine(e);
Console.WriteLine("--------");
}
}
}
open System
open System.Reflection
let limit = 10000000
let primes = PrimeNumberGenerator limit
let start = 1000001
try
let values = primes.GetPrimesFrom start
printfn $"There are {values.Length} prime numbers from {start} to {limit}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
let domain = AppDomain.CreateDomain "Domain2"
let gen =
domain.CreateInstanceAndUnwrap(
typeof<PrimeNumberGenerator>.Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
[| box 1000000 |], null, null)
:?> PrimeNumberGenerator
try
let start = 100
printfn $"{gen.GetPrimesFrom start}"
with :? NotPrimeException as e ->
printfn $"{e.NonPrime} is not prime"
printfn $"{e}"
printfn "--------"
Imports System.Reflection
Module Example
Sub Main()
Dim limit As Integer = 10000000
Dim primes As New PrimeNumberGenerator(limit)
Dim start As Integer = 1000001
Try
Dim values() As Integer = primes.GetPrimesFrom(start)
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit)
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
Dim domain As AppDomain = AppDomain.CreateDomain("Domain2")
Dim gen As PrimeNumberGenerator = domain.CreateInstanceAndUnwrap(
GetType(Example).Assembly.FullName,
"PrimeNumberGenerator", True,
BindingFlags.Default, Nothing,
{1000000}, Nothing, Nothing)
Try
start = 100
Console.WriteLine(gen.GetPrimesFrom(start))
Catch e As NotPrimeException
Console.WriteLine("{0} is not prime", e.NonPrime)
Console.WriteLine(e)
Console.WriteLine("--------")
End Try
End Sub
End Module
' The example displays the following output:
' 1000001 is not prime
' NotPrimeException: 1000001 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
' 100 is not prime
' NotPrimeException: 100 is not a prime number.
' at PrimeNumberGenerator.GetPrimesFrom(Int32 prime)
' at Example.Main()
' --------
Exempel
I följande exempel visas ett catch block (with i F#) som har definierats för att hantera ArithmeticException fel. Det här catch blocket fångar också upp DivideByZeroException fel, eftersom DivideByZeroException härleds från ArithmeticException och det inte finns något catch uttryckligt definierat block för DivideByZeroException fel.
using System;
class ExceptionTestClass
{
public static void Main()
{
int x = 0;
try
{
int y = 100 / x;
}
catch (ArithmeticException e)
{
Console.WriteLine($"ArithmeticException Handler: {e}");
}
catch (Exception e)
{
Console.WriteLine($"Generic Exception Handler: {e}");
}
}
}
/*
This code example produces the following results:
ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
at ExceptionTestClass.Main()
*/
module ExceptionTestModule
open System
let x = 0
try
let y = 100 / x
()
with
| :? ArithmeticException as e ->
printfn $"ArithmeticException Handler: {e}"
| e ->
printfn $"Generic Exception Handler: {e}"
// This code example produces the following results:
// ArithmeticException Handler: System.DivideByZeroException: Attempted to divide by zero.
// at <StartupCode$fs>.$ExceptionTestModule.main@()
Class ExceptionTestClass
Public Shared Sub Main()
Dim x As Integer = 0
Try
Dim y As Integer = 100 / x
Catch e As ArithmeticException
Console.WriteLine("ArithmeticException Handler: {0}", e.ToString())
Catch e As Exception
Console.WriteLine("Generic Exception Handler: {0}", e.ToString())
End Try
End Sub
End Class
'
'This code example produces the following results:
'
'ArithmeticException Handler: System.OverflowException: Arithmetic operation resulted in an overflow.
' at ExceptionTestClass.Main()
'