다음을 통해 공유


System.Exception 클래스

이 문서에서는 이 API에 대한 참조 설명서에 대한 추가 설명서를 제공합니다.

Exception 클래스는 모든 예외의 기본 클래스입니다. 오류가 발생 하는 시스템 또는 현재 실행 중인 애플리케이션 오류에 대 한 정보를 포함 하는 예외를 throw 하 여 보고 합니다. 예외가 throw 되 면 애플리케이션에서 또는 기본 예외 핸들러에서 처리 됩니다.

오류 및 예외

런타임 오류는 다양한 이유로 발생할 수 있습니다. 그러나 코드에서 모든 오류를 예외로 처리해야 하는 것은 아닙니다. 다음은 런타임에 발생할 수 있는 오류의 몇 가지 범주와 이에 대응하는 적절한 방법입니다.

  • 사용 오류입니다. 사용 오류는 예외가 발생할 수 있는 프로그램 논리의 오류를 나타냅니다. 그러나 오류는 예외 처리를 통해서가 아니라 잘못된 코드를 수정하여 해결해야 합니다. 예를 들어 다음 예제에서 메서드의 재정의 Object.Equals(Object) 는 인수가 항상 null이 obj 아니어야 한다고 가정합니다.

    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: {0}", 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
    

    재정의 NullReferenceExceptionobj 호출 Object.Equals 한 다음 다시 컴파일하기 전에 null을 명시적으로 테스트하도록 소스 코드를 수정하여 결과를 제거할 수 있는 예외입니다null. 다음 예제에는 인수를 처리하는 수정된 소스 코드가 포함되어 있습니다 null .

    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: {0}", 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
    

    사용 오류에 예외 처리를 사용하는 대신 디버그 빌드에서 사용 오류를 식별하는 방법과 디버그 및 Trace.Assert 릴리스 빌드 모두에서 사용 오류를 식별하는 방법을 사용할 Debug.Assert 수 있습니다. 자세한 내용은 관리 코드의 어설션을 참조하세요.

  • 프로그램 오류입니다. 프로그램 오류는 버그 없는 코드를 작성하여 반드시 피할 수 없는 런타임 오류입니다.

    경우에 따라 프로그램 오류는 예상 또는 일상적인 오류 조건을 반영할 수 있습니다. 이 경우 예외 처리를 사용하여 프로그램 오류를 처리하지 않고 작업을 다시 시도하는 것이 좋습니다. 예를 들어 사용자가 특정 형식으로 날짜를 입력해야 하는 경우 메서드를 호출 DateTime.TryParseExact 하여 날짜 문자열을 구문 분석할 수 있습니다. 이 메서드는 메서드를 사용하는 DateTime.ParseExact 대신 구문 분석 작업이 성공했는지 여부를 나타내는 값을 반환 Boolean 합니다. 이 값은 날짜 문자열을 값으로 변환할 수 없는 경우 예외를 DateTime throw합니다FormatException. 마찬가지로, 사용자가 존재하지 않는 파일을 열려고 하면 먼저 메서드를 호출 File.Exists 하여 파일이 있는지 여부를 검사, 파일이 없는 경우 만들 것인지 묻는 메시지를 표시할 수 있습니다.

    다른 경우에 프로그램 오류는 코드에서 처리할 수 있는 예기치 않은 오류 조건을 반영합니다. 예를 들어 파일이 있는지 확인하기 위해 검사 있더라도 파일을 열기 전에 삭제되거나 손상되었을 수 있습니다. 이 경우 개체를 인스턴스화하거나 메서드를 StreamReader 호출하여 파일을 열려고 하면 예외가 OpenFileNotFoundException 발생할 수 있습니다. 이러한 경우 예외 처리를 사용하여 오류에서 복구해야 합니다.

  • 시스템 오류. 시스템 오류는 의미 있는 방식으로 프로그래밍 방식으로 처리할 수 없는 런타임 오류입니다. 예를 들어 공용 언어 런타임에서 추가 메모리를 OutOfMemoryException 할당할 수 없는 경우 모든 메서드에서 예외를 throw할 수 있습니다. 일반적으로 시스템 오류는 예외 처리를 사용하여 처리되지 않습니다. 대신,와 같은 이벤트를 사용할 수 있습니다 AppDomain.UnhandledException 호출을 Environment.FailFast 예외 정보를 기록 하 고 애플리케이션을 종료 하기 전에 실패의 사용자에 게 알릴 방법입니다.

블록 시도/catch

공용 언어 런타임은 예외를 개체로 표현하고 프로그램 코드와 예외 처리 코드를 블록과 catch 블록으로 try 분리하는 예외 처리 모델을 제공합니다. 각각 특정 유형의 예외를 처리하도록 설계된 하나 이상의 catch 블록 또는 다른 블록보다 더 구체적인 예외를 catch하도록 설계된 하나의 블록이 있을 수 있습니다.

내 코드에 존재 해야 애플리케이션의 애플리케이션 코드 블록을 실행 하는 동안 발생 하는 예외를 처리 하는 경우는 try 문 이라고는 try 블록입니다. throw 된 예외를 처리 하는 애플리케이션 코드를 try 블록 내에 배치 됩니다는 catch 문 라고는 catch 블록입니다. 0개 이상의 catch 블록이 블록과 try 연결되며 각 catch 블록에는 블록이 처리하는 예외 유형을 결정하는 형식 필터가 포함됩니다.

예외가 발생 하는 경우는 try 블록에 시스템 연결 된 검색 catch 블록을 찾을 때까지 애플리케이션 코드에 나타나는 순서에는 catch 예외를 처리 하는 블록입니다. 블록은 catch catch 블록의 형식 T 필터가 지정 T 하거나 파생되는 형식인 경우 형식의 예외를 T 처리합니다. 시스템에서 예외를 처리하는 첫 번째 catch 블록을 찾은 후 검색을 중지합니다. 이러한 이유로 애플리케이션 코드를 catch 전에 형식을 처리 하는 블록을 지정 해야 합니다는 catch 이 섹션에 나오는 예제에서 설명한 것 처럼 해당 기본 형식으로 처리 하는 블록. 마지막으로 핸들을 지정하는 System.Exception catch 블록입니다.

현재 try 블록과 연결된 블록 중 catch 어느 것도 예외를 처리하지 않고 현재 블록이 현재 try 호출 catch 의 다른 try 블록 내에 중첩되면 다음 바깥쪽 try 블록과 연결된 블록이 검색됩니다. 예외에 대한 블록이 없 catch 으면 시스템은 현재 호출에서 이전 중첩 수준을 검색합니다. 현재 호출에서 예외에 대한 블록이 없 catch 으면 예외가 호출 스택 위로 전달되고 이전 스택 프레임은 예외를 catch 처리하는 블록을 검색합니다. 호출 스택의 검색은 예외가 처리되거나 호출 스택에 더 이상 프레임이 없을 때까지 계속됩니다. 찾지 못한 채 호출 스택의 위쪽에 도달 하면는 catch 기본 예외 핸들러가 예외를 처리 하는 블록을 처리 하 고 애플리케이션을 종료 합니다.

F# try.. with 식

F#은 블록을 사용하지 catch 않습니다. 대신, 발생한 예외는 단일 with 블록을 사용하여 패턴과 일치합니다. 문이 아닌 식이므로 모든 경로가 동일한 형식을 반환해야 합니다. 자세히 알아보려면 시도...를 참조 하세요. 식과 함께 사용합니다.

예외 유형 기능

예외 유형은 다음 기능을 지원합니다.

  • 오류를 설명하는 사람이 읽을 수 있는 텍스트입니다. 예외가 발생하면 런타임에서 사용자에게 오류의 특성을 알리고 문제를 해결하기 위한 작업을 제안하는 문자 메시지를 사용할 수 있게 합니다. 이 문자 메시지는 예외 개체의 Message 속성에 보관됩니다. 예외 개체를 만드는 동안 해당 특정 예외의 세부 정보를 설명하는 텍스트 문자열을 생성자에 전달할 수 있습니다. 생성자에 오류 메시지 인수가 제공되지 않으면 기본 오류 메시지가 사용됩니다. 자세한 내용은 Message 속성을 참조하세요.

  • 예외가 throw된 호출 스택의 상태입니다. 이 속성은 StackTrace 코드에서 오류가 발생하는 위치를 확인하는 데 사용할 수 있는 스택 추적을 전달합니다. 스택 추적에는 호출된 모든 메서드와 호출이 이루어지는 원본 파일의 줄 번호가 나열됩니다.

예외 클래스 속성

클래스에는 Exception 코드 위치, 형식, 도움말 파일 및 예외 HelpLinkStackTraceMessageDataInnerExceptionHResultSourceTargetSite이유를 식별하는 데 도움이 되는 다양한 속성이 포함됩니다.

두 개 이상의 예외 사이에 인과 관계가 있는 경우 속성은 InnerException 이 정보를 기본. 외부 예외는 이 내부 예외에 대한 응답으로 throw됩니다. 외부 예외를 처리하는 코드는 이전 내부 예외의 정보를 사용하여 오류를 보다 적절하게 처리할 수 있습니다. 예외에 대한 추가 정보는 속성에 키/값 쌍의 컬렉션으로 저장할 수 있습니다 Data .

예외 개체를 만드는 동안 생성자에 전달되는 오류 메시지 문자열은 지역화되어야 하며 클래스를 사용하여 ResourceManager 리소스 파일에서 제공할 수 있습니다. 지역화된 리소스에 대한 자세한 내용은 위성 어셈블리 만들기 및 리소스 패키징 및 배포 항목을 참조하세요.

사용자에게 예외가 발생한 이유에 대한 광범위한 정보를 제공하기 위해 속성은 HelpLink 도움말 파일에 대한 URL(또는 URN)을 보유할 수 있습니다.

클래스는 Exception 값이 0x80131500 HRESULT COR_E_EXCEPTION를 사용합니다.

클래스 인스턴스 Exception 의 초기 속성 값 목록은 생성자를 참조 Exception 하세요.

성능 고려 사항

예외를 throw하거나 처리하면 상당한 양의 시스템 리소스와 실행 시간이 사용됩니다. 예측 가능한 이벤트 또는 흐름 제어를 처리하지 않고 진정으로 특별한 조건을 처리하기 위해서만 예외를 throw합니다. 예를 들어 클래스 라이브러리를 개발할 때와 같은 경우에 메서드 인수가 유효하지 않은 경우 유효한 매개 변수를 사용하여 메서드를 호출해야 하므로 예외를 throw하는 것이 합리적입니다. 잘못된 메서드 인수가 사용 오류의 결과가 아닌 경우 특별한 문제가 발생했음을 의미합니다. 반대로, 사용자가 잘못된 데이터를 입력할 것으로 예상할 수 있으므로 사용자 입력이 잘못된 경우 예외를 throw하지 마세요. 대신 사용자가 유효한 입력을 입력할 수 있도록 재시도 메커니즘을 제공합니다. 예외를 사용하여 사용 오류를 처리해서는 안 됩니다. 대신 어설션을 사용하여 사용 오류를 식별하고 수정합니다.

또한 반환 코드가 충분한 경우 예외를 throw하지 마세요. 반환 코드를 예외로 변환하지 마세요. 은 정기적으로 예외를 catch하지 않고 무시한 다음 처리를 계속합니다.

예외 다시 throw

대부분의 경우 예외 처리기는 예외를 호출자에게 전달하려고 합니다. 이 문제는 다음에서 가장 자주 발생합니다.

  • .NET 클래스 라이브러리 또는 다른 클래스 라이브러리의 메서드에 대한 호출을 차례로 래핑하는 클래스 라이브러리입니다.

  • 애플리케이션 또는 심각한 예외를 발생 하는 라이브러리입니다. 예외 처리기는 예외를 기록한 다음 예외를 다시 throw할 수 있습니다.

예외를 다시 throw하는 권장 방법은 단순히 C#의 throw 문, F#의 reraise 함수 및 식을 포함하지 않고 Visual Basic의 Throw 문을 사용하는 것입니다. 이렇게 하면 예외가 호출자에게 전파될 때 모든 호출 스택 정보가 유지됩니다. 다음 예제에서는 이것을 보여 줍니다. 문자열 확장 메서드 FindOccurrences는 인수의 유효성을 미리 검사하지 않고 하나 이상의 호출 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

그런 다음 호출자가 두 번 호출합니다 FindOccurrences . 두 번째 호출 FindOccurrences에서 호출자는 검색 문자열로 전달 null 되어 메서드가 String.IndexOf(String, Int32) 예외를 ArgumentNullException throw합니다. 이 예외는 메서드에서 FindOccurrences 처리되고 호출자에게 다시 전달됩니다. throw 문은 식 없이 사용되므로 예제의 출력은 호출 스택이 유지됨을 보여줍니다.

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 ({0}) occurred.",
                              e.GetType().Name);
            Console.WriteLine("Message:\n   {0}\n", e.Message);
            Console.WriteLine("Stack Trace:\n   {0}\n", e.StackTrace);
        }
    }

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

반면, 다음 문을 사용하여 예외가 다시 throw되는 경우:

throw e;
Throw e
raise e

... 그런 다음 전체 호출 스택이 유지되지 않고 예제에서 다음 출력을 생성합니다.

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

약간 더 번거로운 대안은 새 예외를 throw하고 내부 예외에서 원래 예외의 호출 스택 정보를 유지하는 것입니다. 그런 다음 호출자는 새 예외의 InnerException 속성을 사용하여 스택 프레임 및 원래 예외에 대한 기타 정보를 검색할 수 있습니다. 이 경우 throw 문은 다음과 같습니다.

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)

예외를 처리하는 사용자 코드는 다음 예외 처리기에서 보여 주듯이 속성에 원래 예외에 대한 정보가 포함되어 있음을 InnerException 알아야 합니다.

try
{
    indexes = s.FindOccurrences(toFind);
    ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
    Console.WriteLine("An exception ({0}) occurred.",
                      e.GetType().Name);
    Console.WriteLine("   Message:\n{0}", e.Message);
    Console.WriteLine("   Stack Trace:\n   {0}", e.StackTrace);
    Exception ie = e.InnerException;
    if (ie != null)
    {
        Console.WriteLine("   The Inner Exception:");
        Console.WriteLine("      Exception Name: {0}", ie.GetType().Name);
        Console.WriteLine("      Message: {0}\n", ie.Message);
        Console.WriteLine("      Stack Trace:\n   {0}\n", ie.StackTrace);
    }
}
// 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)

표준 예외 선택

예외를 throw해야 하는 경우 사용자 지정 예외를 구현하는 대신 .NET에서 기존 예외 형식을 사용할 수 있습니다. 다음 두 가지 조건에서 표준 예외 형식을 사용해야 합니다.

  • 사용 오류(즉, 메서드를 호출하는 개발자가 만든 프로그램 논리의 오류)로 인해 발생하는 예외를 throw합니다. 일반적으로 , ArgumentNullException, InvalidOperationException또는 NotSupportedException.와 같은 ArgumentException예외를 throw합니다. 예외 개체를 인스턴스화할 때 예외 개체의 생성자에 제공하는 문자열은 개발자가 오류를 수정할 수 있도록 오류를 설명해야 합니다. 자세한 내용은 Message 속성을 참조하세요.

  • 기존 .NET 예외를 사용하여 호출자에게 전달할 수 있는 오류를 처리하고 있습니다. 가능한 가장 파생된 예외를 throw해야 합니다. 예를 들어 메서드에 인수가 열거형 형식의 유효한 멤버여야 하는 경우 인수가 아닌 ArgumentException(가장 파생된 클래스)를 throw InvalidEnumArgumentException 해야 합니다.

다음 표에서는 일반적인 예외 유형 및 예외를 throw할 조건을 나열합니다.

예외 조건
ArgumentException 메서드에 전달되는 null이 아닌 인수가 잘못되었습니다.
ArgumentNullException 메서드에 전달되는 인수는 .입니다 null.
ArgumentOutOfRangeException 인수가 유효한 값 범위를 벗어났습니다.
DirectoryNotFoundException 디렉터리 경로의 일부가 잘못되었습니다.
DivideByZeroException 정수 또는 Decimal 나누기 작업의 분모는 0입니다.
DriveNotFoundException 드라이브를 사용할 수 없거나 존재하지 않습니다.
FileNotFoundException 파일이 없습니다.
FormatException 값은 다음과 같은 Parse변환 메서드에 의해 문자열에서 변환할 적절한 형식이 아닙니다.
IndexOutOfRangeException 인덱스가 배열 또는 컬렉션의 범위를 벗어났습니다.
InvalidOperationException 개체의 현재 상태에서 메서드 호출이 잘못되었습니다.
KeyNotFoundException 컬렉션의 멤버에 액세스하기 위해 지정된 키를 찾을 수 없습니다.
NotImplementedException 메서드 또는 작업이 구현되지 않습니다.
NotSupportedException 메서드 또는 작업은 지원되지 않습니다.
ObjectDisposedException 삭제된 개체에 대해 작업이 수행됩니다.
OverflowException 산술, 캐스팅 또는 변환 연산으로 인해 오버플로가 발생합니다.
PathTooLongException 경로 또는 파일 이름이 시스템 정의 최대 길이를 초과합니다.
PlatformNotSupportedException 현재 플랫폼에서는 작업이 지원되지 않습니다.
RankException 잘못된 차원 수를 가진 배열이 메서드에 전달됩니다.
TimeoutException 작업에 할당된 시간 간격이 만료되었습니다.
UriFormatException 잘못된 URI(Uniform Resource Identifier)가 사용됩니다.

사용자 지정 예외 구현

다음 경우 기존 .NET 예외를 사용하여 오류 조건을 처리하는 것은 적절하지 않습니다.

  • 예외가 기존 .NET 예외에 매핑할 수 없는 고유한 프로그램 오류를 반영하는 경우

  • 예외에 기존 .NET 예외에 적합한 처리와 다른 처리가 필요하거나 유사한 예외에서 예외를 명확하게 구분해야 하는 경우 예를 들어 대상 정수 형식 범위를 벗어난 문자열의 숫자 표현을 구문 분석할 때 예외를 throw ArgumentOutOfRangeException 하는 경우 호출자가 메서드를 호출할 때 적절한 제한된 값을 제공하지 않는 오류에 대해 동일한 예외를 사용하지 않으려는 것입니다.

Exception 클래스는 .NET에서 모든 예외의 기본 클래스입니다. 많은 파생 클래스는 클래스 멤버의 Exception 상속된 동작을 사용하며 멤버를 재정의 Exception하지 않으며 고유한 멤버를 정의하지도 않습니다.

고유한 예외 클래스를 정의하려면 다음을 수행합니다.

  1. 에서 Exception상속되는 클래스를 정의합니다. 필요한 경우 예외에 대한 추가 정보를 제공하기 위해 클래스에 필요한 고유 멤버를 정의합니다. 예를 들어 ArgumentException 클래스에는 ParamName 인수로 인해 예외가 발생한 매개 변수의 이름을 지정하는 속성이 포함되며 RegexMatchTimeoutException , 이 속성에는 제한 시간 간격을 MatchTimeout 나타내는 속성이 포함됩니다.

  2. 필요한 경우 기능을 변경하거나 수정하려는 상속된 멤버를 재정의합니다. 대부분의 기존 파생 클래스는 Exception 상속된 멤버의 동작을 재정의하지 않습니다.

  3. 사용자 지정 예외 개체를 직렬화할 수 있는지 여부를 확인합니다. Serialization을 사용하면 예외에 대한 정보를 저장할 수 있으며 원격 컨텍스트에서 서버 및 클라이언트 프록시에서 예외 정보를 공유할 수 있습니다. 예외 개체를 직렬화할 수 있도록 하려면 특성으로 SerializableAttribute 표시합니다.

  4. 예외 클래스의 생성자를 정의합니다. 일반적으로 예외 클래스에는 다음 생성자 중 하나 이상이 있습니다.

    • Exception()- 기본값을 사용하여 새 예외 개체의 속성을 초기화합니다.

    • Exception(String)지정된 오류 메시지를 사용하여 새 예외 개체를 초기화하는 입니다.

    • Exception(String, Exception)지정된 오류 메시지 및 내부 예외를 사용하여 새 예외 개체를 초기화하는 입니다.

    • Exception(SerializationInfo, StreamingContext)protected serialize된 데이터에서 새 예외 개체를 초기화하는 생성자입니다. 예외 개체를 직렬화할 수 있도록 선택한 경우 이 생성자를 구현해야 합니다.

다음 예제에서는 사용자 지정 예외 클래스의 사용을 보여 줍니다. 소수가 아닌 시작 번호를 지정하여 클라이언트가 소수 시퀀스를 검색하려고 할 때 throw되는 예외를 정의 NotPrimeException 합니다. 예외는 예외를 발생시킨 소수가 아닌 숫자를 반환하는 새 속성을 NonPrime정의합니다. 클래스는 보호된 매개 변수가 없는 생성자와 serialization NotPrimeException 을 위한 매개 변수 및 StreamingContextSerializationInfo 매개 변수를 구현하는 것 외에도 속성을 지원하는 NonPrime 세 개의 추가 생성자를 정의합니다. 각 생성자는 소수가 아닌 값의 값을 유지하는 것 외에도 기본 클래스 생성자를 호출합니다. NotPrimeException 또한 클래스는 특성으로 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

다음 예제에 표시된 클래스는 PrimeNumberGenerator Eratosthenes의 Sieve를 사용하여 소수 시퀀스를 2에서 해당 클래스 생성자에 대한 호출에서 클라이언트가 지정한 제한으로 계산합니다. 이 메서드는 GetPrimesFrom 지정된 하한보다 크거나 같은 소수를 모두 반환하지만 하위 제한이 소수가 아닌 경우 throw합니다 NotPrimeException .

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

두 번 호출 하는 다음 예제는 GetPrimesFrom 메서드 사용 하 여 소수가 아닌 숫자를 애플리케이션 도메인 경계를 교차 하는 중입니다. 두 경우 모두 예외가 throw되고 클라이언트 코드에서 성공적으로 처리됩니다.

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 {0} prime numbers from {1} to {2}",
                              start, limit);
        }
        catch (NotPrimeException e)
        {
            Console.WriteLine("{0} is not prime", e.NonPrime);
            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("{0} is not prime", e.NonPrime);
            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()
'      --------

예제

다음 예제에서는 오류를 처리 ArithmeticException 하도록 정의된 (withF#) 블록을 보여 catch 줍니다. 이 catch 블록은 오류에서 ArithmeticException 파생되고 오류에 대해 DivideByZeroException 명시적으로 정의된 블록이 없기 catch 때문에 DivideByZeroException 오류를 catch DivideByZeroException 합니다.

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