Megosztás a következőn keresztül:


System.Exception osztály

Ez a cikk kiegészítő megjegyzéseket tartalmaz az API referenciadokumentációjához.

Az Exception osztály az összes kivétel alaposztálya. Hiba esetén a rendszer vagy a jelenleg futó alkalmazás kivételt dob, amely információkat tartalmaz a hibáról. A kivételt az alkalmazás vagy az alapértelmezett kivételkezelő kezeli.

Hibák és kivételek

A futásidejű hibák többféle okból is előfordulhatnak. Nem minden hibát kell azonban kivételként kezelni a kódban. Íme néhány hibakategória, amelyek futásidőben fordulhatnak elő, és a megfelelő válaszlehetőségek.

  • Használati hibák. A használati hiba a programlogika olyan hibáját jelenti, amely kivételt eredményezhet. A hibát azonban nem kivételkezeléssel, hanem a hibás kód módosításával kell elhárítani. Például, a következő példában a Object.Equals(Object) metódus felülbírálása azt feltételezi, hogy az obj argumentumnak mindig nemnullának kell lennie.

    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 Module
    

    Az NullReferenceException kivétel, amely akkor keletkezik, ha objnull, kiküszöbölhető a forráskód módosításával úgy, hogy explicit módon teszteljük a null értéket, mielőtt meghívnánk a Object.Equals felülbírálást, majd újrafordítjuk azt. Az alábbi példa tartalmazza a null argumentumot kezelő javított forráskódot.

    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: False
    
    Public 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: False
    

    A használati hibák kivételkezelése helyett a módszerrel azonosíthatja a Debug.Assert hibakeresési buildek használati hibáit, és azonosíthatja a Trace.Assert használati hibákat a hibakeresési és kiadási buildekben. További információért lásd: Állítások a felügyelt kódban.

  • Programhibák. A programhibák olyan futásidejű hibák, amelyeket nem feltétlenül lehet elkerülni a hibamentes kód megírásával.

    Bizonyos esetekben a programhibák egy várt vagy rutinszerű hibaállapotra utalhatnak. Ebben az esetben érdemes lehet elkerülni a kivételkezelés használatát a programhiba kezeléséhez, és ehelyett próbálkozzon újra a művelettel. Ha például a felhasználónak egy adott formátumban kell megadnia egy dátumot, a metódus meghívásával DateTime.TryParseExact elemezheti a dátumsztringet, amely egy értéket ad vissza Boolean , amely jelzi, hogy az elemzési művelet sikeres volt-e a metódus használata DateTime.ParseExact helyett, ami kivételt FormatException jelent, ha a dátumsztring nem konvertálható értékké DateTime . Hasonlóképpen, ha egy felhasználó nem létező fájlt próbál megnyitni, először meghívhatja a File.Exists metódust annak ellenőrzéséhez, hogy létezik-e a fájl, és ha nem, kérje meg a felhasználót, hogy létre szeretné-e hozni.

    Más esetekben a programhibák egy váratlan hibafeltételt tükröznek, amely a kódban kezelhető. Ha például ellenőrizte, hogy létezik-e fájl, akkor is törölhető, mielőtt megnyithatja, vagy megsérülhet. Ebben az esetben a StreamReader objektum példányosításával vagy a Open metódus meghívásával megpróbálni megnyitni a fájlt FileNotFoundException kivételt okozhat. Ezekben az esetekben kivételkezeléssel kell helyreállítani a hibát.

  • Rendszerhibák. A rendszerhiba olyan futásidejű hiba, amely nem kezelhető programozott módon, értelmes módon. Egy metódus például kivételt OutOfMemoryException okozhat, ha a közös nyelvi futtatókörnyezet nem tud további memóriát lefoglalni. A rendszerhibák általában nem kivételkezeléssel kezelhetők. Ehelyett használhat például egy eseményt AppDomain.UnhandledException , és meghívhatja a metódust a Environment.FailFast kivételadatok naplózására, és az alkalmazás leállása előtt értesítheti a felhasználót a hibáról.

Kipróbálás/fogási blokkok

A közös nyelvi futtatókörnyezet egy kivételkezelési modellt biztosít, amely a kivételek objektumként való ábrázolásán, valamint a programkód és a kivételkezelő kód blokkokra és try blokkokra való catch elkülönítésén alapul. Lehet egy vagy több catch blokk, amelyek mindegyike egy adott kivételtípus kezelésére van kialakítva, vagy egy blokk, amely egy másik blokknál pontosabb kivételt fog el.

Ha egy alkalmazás kezeli az alkalmazáskódblokk végrehajtása során előforduló kivételeket, a kódot egy try utasításban kell elhelyezni, és blokknak try kell nevezni. A try blokk által kidobott kivételeket kezelő alkalmazáskód egy catch utasításba kerül, és catch blokknak nevezzük. Egy blokkhoz catch nulla vagy több try blokk van társítva, és minden catch blokk tartalmaz egy típusszűrőt, amely meghatározza, hogy milyen típusú kivételeket kezel.

Ha kivétel történik egy try blokkban, a rendszer a társított catch blokkokat az alkalmazáskódban megjelenő sorrendben keresi, amíg meg nem találja catch a kivételt kezelő blokkot. A catch blokk a típuskivételt T kezeli, ha a fogási blokk típusszűrője megadja T, vagy bármely olyan típust, amelyből T származhat. A rendszer leállítja a keresést, miután megtalálta a kivételt kezelő első catch blokkot. Ezért az alkalmazáskódban egy catch típust kezelő blokkot kell megadni az catch alaptípusokat kezelő blokk előtt, ahogyan azt az ebben a szakaszban ismertetett példában is bemutatjuk. Egy kivételkezelő blokk, amely kezeli a System.Exception-t, utoljára van megadva.

Ha az aktuális catch blokkhoz társított try blokkok egyike sem kezeli a kivételt, és az aktuális try blokk más try blokkokba van ágyazva az aktuális hívás során, akkor az ezekhez a blokkokhoz kapcsolódó következő beágyazó catch blokkokat keresi meg a rendszer. Ha nem található catch blokk a kivételhez tartozóan, a rendszer keresi az aktuális hívás korábbi beágyazási szinteket. Ha az aktuális hívásban nem található catch blokk a kivételhez, a kivételt felfelé továbbítják a hívásveremben, és az előző veremkeretben keresnek egy catch blokkot, amely kezeli a kivételt. A hívásverem keresése addig folytatódik, amíg a kivételt nem kezelik, vagy amíg nincs több keret a hívásveremen. Ha a hívásverem tetejét úgy éri el, hogy nem talál egy catch kivételt kezelő blokkot, az alapértelmezett kivételkezelő kezeli azt, és az alkalmazás leáll.

F# try.. kifejezéssel

Az F# nem használ catch blokkokat. Ehelyett az emelt kivétel mintája egyetlen with blokk használatával van egyeztetve. Mivel ez egy kifejezés, és nem utasítás, minden elérési útnak ugyanazt a típust kell visszaadnia. További tudnivalókért lásd: A try...with kifejezés.

Kivételtípus-funkciók

A kivételtípusok a következő funkciókat támogatják:

  • A hibát leíró, emberi olvasásra alkalmas szöveg. Kivétel esetén a futtatókörnyezet elérhetővé tesz egy szöveges üzenetet, amely tájékoztatja a felhasználót a hiba természetéről, és műveletet javasol a probléma megoldásához. Ez a szöveges üzenet a Message kivételobjektum tulajdonságában van tárolva. A kivételobjektum létrehozása során szöveges sztringet adhat át a konstruktornak az adott kivétel részleteinek leírásához. Ha a konstruktor nem ad meg hibaüzenet argumentumot, a rendszer az alapértelmezett hibaüzenetet használja. További információért lásd a Message tulajdonságot.

  • A hívásverem állapota, amikor a kivétel ki lett dobva. A StackTrace tulajdonság egy veremnyomot hordoz, amely segítségével megállapítható, hol fordul elő a hiba a kódban. A veremnyom felsorolja az összes meghívott metódust és a forrásfájlban található sorokat, ahol ezek a hívások történnek.

Kivételosztály tulajdonságai

Az Exception osztály számos tulajdonságot tartalmaz, amelyek segítenek azonosítani a kód helyét, típusát, a súgófájlt és a kivétel okát: StackTrace, , InnerException, Message, HelpLink, HResult, SourceTargetSiteés Data.

Ha két vagy több kivétel között okozati kapcsolat áll fenn, a tulajdonság megőrzi ezeket az InnerException információkat. Ennek a belső kivételnek a következményeként dobják a külső kivételt. A külső kivételt kezelő kód a korábbi belső kivétel információit használhatja a hiba megfelelőbb kezelésére. A kivételre vonatkozó kiegészítő információk kulcs-/értékpárok gyűjteményeként tárolhatók a Data tulajdonságban.

A kivételobjektum létrehozása során a konstruktornak átadott hibaüzenet-sztringnek honosítottnak kell lennie, és az ResourceManager osztály használatával egy erőforrásfájlból is megadható. A honosított erőforrásokról további információt a Műholdas szerelvények létrehozása , valamint az Erőforrások csomagolása és üzembe helyezése témakörökben talál.

Ha a felhasználónak átfogó információt szeretne nyújtani a kivétel okáról, a HelpLink tulajdonság tartalmazhat egy URL-t (vagy URN-t) egy súgófájlhoz.

Az Exception osztály a HRESULT COR_E_EXCEPTIONértéket használja, amelynek értéke 0x80131500.

Az osztály egy példányának kezdeti tulajdonságértékeinek Exception listájáért tekintse meg a Exception konstruktorokat.

Teljesítménnyel kapcsolatos szempontok

A kivétel kivetése vagy kezelése jelentős mennyiségű rendszererőforrást és végrehajtási időt igényel. Kivételeket csak a valóban rendkívüli feltételek kezelésére, a kiszámítható események vagy a folyamatvezérlés kezelésére nem szabad kivenni. Bizonyos esetekben például, például az osztálytár fejlesztésekor célszerű kivételt tenni, ha egy metódusargumentum érvénytelen, mert a metódust érvényes paraméterekkel kell meghívni. Érvénytelen metódusargumentum, ha nem használati hiba eredménye, akkor valami rendkívüli esemény történt. Ezzel szemben ne adjon kivételt, ha a felhasználói bevitel érvénytelen, mert előfordulhat, hogy a felhasználók időnként érvénytelen adatokat írnak be. Ehelyett adjon meg újrapróbálkozásos mechanizmust, hogy a felhasználók érvényes bemenetet adjanak meg. A használati hibák kezelésére nem szabad kivételeket használnia. Ehelyett használjon állításokat a használati hibák azonosítására és javítására.

Ezenkívül ne tegyen kivételt, ha a visszatérési kód elegendő; ne konvertálja a visszatérési kódot kivételsé; és ne észlelje rutinszerűen a kivételt, hagyja figyelmen kívül, majd folytassa a feldolgozást.

Kivétel ismételt elvetése

A kivételkezelő sok esetben egyszerűen csak át szeretné adni a kivételt a hívónak. Ez leggyakrabban a következő esetekben fordul elő:

  • Osztálytár, amely a .NET-osztálytárban vagy más osztálytárakban lévő metódusokra irányuló hívásokat tördeli.

  • Olyan alkalmazás vagy tár, amely végzetes kivételt tapasztal. A kivételkezelő naplózhatja a kivételt, majd újradobhatja a kivételt.

A kivétel újbóli eldobásának ajánlott módja, ha egyszerűen használja C#-ban a throw utasítást, F#-ban a reraise függvényt és Visual Basic-ben a Throw utasítást, kifejezés nélkül. Ez biztosítja, hogy a hívásverem összes információja megmaradjon, amikor a kivétel átadásra kerül a hívóhoz. Az alábbi példa ezt szemlélteti. A karakterlánc kiterjesztési módszer, FindOccurrences, egy vagy több hívást hajt végre anélkül, hogy az argumentumait előzetesen érvényesítené String.IndexOf(String, Int32).

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

A hívó ezután kétszer hív FindOccurrences . A második hívásban a FindOccurrences hívó egy null keresési sztringet ad át, ami miatt a String.IndexOf(String, Int32) metódus egy ArgumentNullException kivételt dob. Ezt a kivételt a FindOccurrences metódus kezeli, és visszaküldi a hívónak. Mivel a dobási utasítás kifejezés nélkül van használva, a példa kimenete azt mutatja, hogy a hívásverem megmarad.

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()

Ezzel szemben, ha a kivételt az alábbi utasítással ismét elveti:

throw e;
Throw e
raise e

... akkor a teljes hívási verem nem marad meg, és a példa a következő kimenetet hozza létre:

'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()

Egy kissé nehézkesebb alternatíva az új kivétel elvetése, és az eredeti kivétel hívásveremadatainak megőrzése egy belső kivételben. A hívó ezután az új kivétel tulajdonságával InnerException lekérheti a veremkeretet és az eredeti kivételre vonatkozó egyéb információkat. Ebben az esetben a dobás a következő:

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)

A kivételt kezelő felhasználói kódnak tudnia kell, hogy a InnerException tulajdonság információkat tartalmaz az eredeti kivételről, ahogyan azt az alábbi kivételkezelő szemlélteti.

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)

Standard kivételek kiválasztása

Ha kivételt kell kivennie, gyakran használhat egy meglévő kivételtípust a .NET-ben az egyéni kivétel implementálása helyett. Az alábbi két feltétel mellett szabványos kivételtípust kell használnia:

  • Kivételt dob, amelyet egy használati hiba okoz (azaz a metódust hívó fejlesztő programlogikai hibája). Általában kivételt kell eldobni, például ArgumentException, ArgumentNullException, InvalidOperationException vagy NotSupportedException. A kivételobjektum példányosításakor a kivételobjektum konstruktorának megadott sztringnek le kell írnia a hibát, hogy a fejlesztő ki tudja javítani. További információért lásd a Message tulajdonságot.

  • Olyan hibát kezel, amely egy meglévő .NET-kivétellel közölhető a hívóval. A lehető legtöbb származtatott kivételt kell elvetnie. Ha például egy metódus megköveteli, hogy egy argumentum érvényes tagja legyen egy enumerálási típusnak, akkor a (leginkább származtatott) osztályt kell dobnia InvalidEnumArgumentException ahelyett, hogy egy ArgumentException.

Az alábbi táblázat a gyakori kivételtípusokat és azokat a feltételeket sorolja fel, amelyek mellett megadná őket.

Kivétel Állapot
ArgumentException A metódusnak átadott nem null argumentum érvénytelen.
ArgumentNullException A metódusnak átadott argumentum az null.
ArgumentOutOfRangeException Az argumentum az érvényes értékek tartományán kívül esik.
DirectoryNotFoundException A könyvtár elérési útjának egy része érvénytelen.
DivideByZeroException Egy egész szám vagy Decimal osztás művelet nevezője nulla.
DriveNotFoundException A meghajtó nem érhető el, vagy nem létezik.
FileNotFoundException Nem létezik fájl.
FormatException Egy érték nincs megfelelő formátumban ahhoz, hogy egy olyan konverziós módszer, mint a Parse, sztringből konvertálni tudja.
IndexOutOfRangeException Az index egy tömb vagy gyűjtemény határain kívül esik.
InvalidOperationException Egy metódushívás érvénytelen az objektum aktuális állapotában.
KeyNotFoundException A gyűjtemény egy tagjának eléréséhez megadott kulcs nem található.
NotImplementedException Egy metódus vagy művelet nincs implementálva.
NotSupportedException Egy metódus vagy művelet nem támogatott.
ObjectDisposedException A művelet egy megsemmisített objektumon történik.
OverflowException Az aritmetikai, öntési vagy konvertálási művelet túlcsordulást eredményez.
PathTooLongException Egy elérési út vagy fájlnév meghaladja a rendszer által megadott maximális hosszt.
PlatformNotSupportedException A művelet nem támogatott az aktuális platformon.
RankException Egy nem megfelelő számú dimenziót tartalmazó tömböt adnak át egy metódusnak.
TimeoutException A műveletre vonatkozó időintervallum lejárt.
UriFormatException Érvénytelen egységes erőforrás-azonosítót (URI) használ a rendszer.

Egyéni kivételek implementálása

A következő esetekben nem megfelelő egy meglévő .NET-kivétel használata a hibafeltételek kezeléséhez:

  • Ha a kivétel olyan egyedi programhibát jelez, amely nem képezhető le meglévő .NET-kivételre.

  • Ha a kivétel olyan kezelést igényel, amely eltér a meglévő .NET-kivételhez megfelelő kezeléstől, vagy a kivételt egy hasonló kivételből kell egyértelműsíteni. Ha például kivételt ArgumentOutOfRangeException ad egy olyan sztring numerikus ábrázolásának elemzésekor, amely a cél integráltípus tartományán kívül esik, nem érdemes ugyanazt a kivételt használni olyan hiba esetén, amely abból adódik, hogy a hívó nem adja meg a megfelelő korlátozott értékeket a metódus meghívásakor.

Az Exception osztály a .NET összes kivételének alaposztálya. Számos származtatott osztály az osztály tagjainak öröklődő viselkedésére Exception támaszkodik; nem bírálja felül az osztály tagjait Exception, és nem határoz meg egyedi tagokat sem.

Saját kivételosztály definiálása:

  1. Definiáljon egy osztályt, amelytől Exceptionöröklődik. Szükség esetén határozza meg az osztályhoz szükséges egyedi tagokat, hogy további információt adjanak a kivételről. Az osztály például tartalmaz egy tulajdonságot, ArgumentException amely megadja annak a paraméternek a nevét, amelynek argumentuma a kivételt okozta, és a ParamName tulajdonság tartalmaz egy RegexMatchTimeoutException tulajdonságot, amely az időtúllépési időközt jelzi.MatchTimeout

  2. Szükség esetén felülbírálhatja azokat az örökölt tagokat, amelyeknek a funkcióját változtatni vagy módosítani szeretné. Vegye figyelembe, hogy a legtöbb meglévő származtatott osztály Exception nem bírálja felül az örökölt tagok viselkedését.

  3. Határozza meg, hogy az egyéni kivételobjektum szerializálható-e. A szerializálás lehetővé teszi a kivételinformációk mentését, és lehetővé teszi, hogy egy kiszolgáló és egy ügyfélproxy megossza a kivételinformációkat egy távoli kommunikációs környezetben. A kivételobjektum szerializálhatóvá tételéhez jelölje meg az SerializableAttribute attribútummal.

  4. Adja meg a kivételosztály konstruktorait. A kivételosztályok általában az alábbi konstruktorok valamelyikét vagy többet tartalmazzák:

    • Exception(), amely alapértelmezett értékeket használ egy új kivételobjektum tulajdonságainak inicializálásához.

    • Exception(String), amely inicializál egy új kivételobjektumot egy megadott hibaüzenettel.

    • Exception(String, Exception), amely inicializál egy új kivételobjektumot egy megadott hibaüzenettel és belső kivétellel.

    • Exception(SerializationInfo, StreamingContext), amely egy protected konstruktor, amely egy új kivételobjektumot inicializál szerializált adatokból. Ezt a konstruktort akkor kell implementálnia, ha úgy döntött, hogy szerializálhatóvá teszi a kivételobjektumot.

Az alábbi példa egy egyéni kivételosztály használatát mutatja be. Olyan kivételt NotPrimeException határoz meg, amely akkor fordul elő, amikor egy ügyfél prímszámok sorozatát próbálja lekérni egy nem prímszámú kezdő szám megadásával. A kivétel egy új tulajdonságot határoz meg, NonPrimeamely a kivételt okozó nem elsődleges számot adja vissza. A védett paraméter nélküli konstruktor és a szerializálás paramétereivel rendelkező SerializationInfoStreamingContext konstruktor implementálása mellett az NotPrimeException osztály három további konstruktort határoz meg a NonPrime tulajdonság támogatásához. Minden konstruktor egy alaposztály-konstruktort hív meg, miközben megőrzi a nem prímszám értékét. Az NotPrimeException osztály az attribútummal SerializableAttribute is meg van jelölve.

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

Az PrimeNumberGenerator alábbi példában látható osztály az Eratosthenes szitával számítja ki a prímszámok sorozatát 2-től az ügyfél által az osztálykonstruktorhoz való hívásban megadott korlátig. A GetPrimesFrom metódus visszaadja az összes olyan prímszámot, amely nagyobb vagy egyenlő egy megadott alsó korlátnál, de ha ez az alsó korlát nem prímszám, akkor NotPrimeException-t dob.

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

Az alábbi példa két, nem elsődleges számokat tartalmazó hívást indít a GetPrimesFrom metódushoz, amelyek közül az egyik átlépi az alkalmazás tartományának határait. A kivétel mindkét esetben ki lesz dobva, és sikeresen kezelhető az ügyfélkódban.

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()
'      --------

Példák

Az alábbi példa egy catch (with F#-ban) blokkot mutat be, amelyet a hibák kezelésére definiáltak ArithmeticException. Ez a catch blokk az DivideByZeroException hibákat is kezeli, mert DivideByZeroException a ArithmeticException származik, és nincs catch blokk explicit módon definiálva a DivideByZeroException hibákhoz.

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()
'