달력

4

« 2024/4 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2016. 8. 26. 16:49

예외 및 예외 처리 프로그래밍/C#2016. 8. 26. 16:49

https://msdn.microsoft.com/ko-kr/library/ms173160.aspx



예외 및 예외 처리


1. C# 언어에서는 프로그램 실행 중에 발생하는 예기치 않은 상황이나 예외 상황을 처리하기 위한 예외 처리 기능을 제공합니다.

2. 예외 처리에는 성공하지 않을 수 있는 작업을 시도하고, 작업을 수행할 이유가 있는지 결정되었을 때 오류를 처리하고, 나중에 리소스를 정리하기 위한 try, catch 및 finally 키워드가 사용됩니다.

3. 예외는 CLR(공용 언어 런타임), .NET Framework나 타사 라이브러리 또는 응용 프로그램 코드에서 발생할 수 있습니다.

4. 이러한 예외는 throw 키워드를 사용하여 생성됩니다.

5. 대부분의 경우 예외는 코드에서 직접 호출한 메서드가 아니라 호출 스택 아래쪽에 있는 다른 메서드에서 throw됩니다.

6. 이런 경우 CLR에서는 스택을 해제하여 특정 예외 형식에 대한 catch 블록이 있는 메서드를 찾고, 찾은 경우 첫 번째 catch 블록을 실행합니다.

7. 호출 스택에서 해당하는 catch 블록을 찾지 못한 경우 CLR은 프로세스를 종료하고 사용자에게 메시지를 표시합니다.

8. 이 예제의 메서드에서는 0으로 나누기 여부를 테스트하고 오류를 catch합니다.

9. 예외를 처리하지 않는다면 이 프로그램은 DivideByZeroException was unhandled 오류 메시지와 함께 종료됩니다.


class ExceptionTest
{
    static double SafeDivision(double x, double y)
    {
        if (y == 0)
            throw new System.DivideByZeroException();
        return x / y;
    }
    static void Main()
    {
        // Input for test purposes. Change the values to see
        // exception handling behavior.
        double a = 98, b = 0;
        double result = 0;

        try
        {
            result = SafeDivision(a, b);
            Console.WriteLine("{0} divided by {1} = {2}", a, b, result);
        }
        catch (DivideByZeroException e)
        {
            Console.WriteLine("Attempted divide by zero.");
        }
    }
}


예외 개요

1. 예외에는 다음과 같은 속성이 있습니다.

1) 예외는 모두 궁극적으로 System.Exception에서 파생되는 형식입니다.

2) 예외가 throw될 가능성이 있는 문 주위에 try 블록을 추가합니다.

3) try 블록 내에서 예외가 발생한 경우 호출 스택에 있는 첫 번째 관련 예외 처리기로 제어 흐름이 이동합니다. C#에서 예외 처리기를 정의하는 데는 catch 키워드가 사용됩니다.

4) 발생한 예외에 대한 예외 처리기가 없으면 프로그램의 실행이 중지되고 오류 메시지가 나타납니다.

5) 예외를 처리할 수 없는 경우 예외를 catch하지 않고 응용 프로그램을 알려진 상태로 두어야 합니다. System.Exception을 catch한 경우 catch 블록 끝에서 throw 키워드를 사용하여 다시 throw합니다.

6) catch 블록에 예외 변수가 정의된 경우 이를 통해 발생한 예외 형식에 대한 자세한 정보를 확인할 수 있습니다.

7) throw 키워드를 사용하면 프로그램에서 예외를 명시적으로 생성할 수 있습니다.

8) 예외 개체에는 호출 스택의 상태와 오류를 설명하는 텍스트를 비롯하여 오류에 대한 자세한 정보가 포함됩다.

9) finally 블록의 코드는 예외가 throw되어도 실행되므로 finally 블록을 사용하여 리소스를 해제합니다. 예를 들어, try 블록에 열려 있는 스트림이나 파일을 닫습니다.

10) .NET Framework의 관리되는 예외는 Win32 구조적 예외 처리 메커니즘을 기반으로 구현됩니다. 자세한 내용은 구조적 예외 처리 (C/C++) 및 A Crash Course on the Depths of Win32 Structured Exception Handling을 참조하십시오.




예외 사용


1. C#의 경우 런타임에 발생하는 프로그램 오류는 예외라는 메커니즘을 사용하여 프로그램을 통해 전파됩니다.

2. 예외는 오류를 발견한 코드에서 throw되고 오류를 수정할 수 있는 코드에서 catch됩니다.

3. 예외는 .NET Framework CLR(공용 언어 런타임)을 통해 throw되거나 프로그램의 코드를 통해 throw될 수 있습니다.

4. throw된 예외는 예외에 대한 catch 문을 만날 때까지 호출 스택을 거슬러 올라가며 전파됩니다.

5. catch되지 않은 예외는 대화 상자를 표시하는 시스템에서 제공하는 제네릭 예외 처리기를 사용하여 처리됩니다.

6. 예외를 나타내는 데는 Exception에서 파생된 클래스가 사용됩니다.

7. 예외의 형식을 식별하는 이 클래스에는 예외에 대한 자세한 정보가 있는 속성이 들어 있습니다.

8. 예외를 throw하려면 예외 파생 클래스의 인스턴스를 만들고 선택적으로 예외의 속성을 구성한 다음 throw 키워드를 사용하여 개체를 throw해야 합니다.

9. 예를 들면 다음과 같습니다.


class CustomException : Exception
{
    public CustomException(string message)
    {

    }

}
private static void TestThrow()
{
    CustomException ex =
        new CustomException("Custom exception in TestThrow()");

    throw ex;
}


10. 예외가 throw되면 런타임에서는 현재 문이 try 블록 내에 있는지 검사합니다.

11. 현재 문이 이 블록 내에 있으면 try 블록과 관련된 catch 블록을 모두 검사하여 예외를 catch할 수 있는지 확인합니다.

12. Catch 블록은 일반적으로 예외 형식을 지정합니다.

13. catch 블록의 형식이 예외의 형식과 동일하거나 예외의 기본 클래스와 동일한 형식이면 catch 블록에서 메서드를 처리할 수 있습니다. 예를 들면 다음과 같습니다.


static void TestCatch()
{
    try
    {
        TestThrow();
    }
    catch (CustomException ex)
    {
        System.Console.WriteLine(ex.ToString());
    }
}


14. 예외를 throw하는 문이 try 블록 안에 있지 않거나 이를 포함하는 try 블록에 대응하는 catch 블록이 없는 경우 런타임에서는 catch 블록과 try 문을 찾기 위해 호출 메서드를 검사합니다.

15. 런타임은 호출 스택을 거슬러 올라가며 호환되는 catch 블록을 계속 찾습니다.

16. catch 블록을 찾아 실행한 후에는 제어가 catch 블록 다음에 있는 문으로 전달됩니다.

17. try 문에는 catch 블록이 여러 개 포함될 수 있습니다.

18. 예외를 처리할 수 있는 첫 번째 catch 문이 실행되고 이후의 catch 문은 호환되는 문이어도 모두 무시됩니다.

19. 따라서, 항상 가장 구체적인(또는 가장 많이 파생되는) 경우부터 가장 일반적인 경우의 순서로 catch 블록을 지정해야 합니다.

20. 예를 들면 다음과 같습니다.


static void TestCatch2()
{
    System.IO.StreamWriter sw = null;
    try
    {
        sw = new System.IO.StreamWriter(@"C:\test\test.txt");
        sw.WriteLine("Hello");
    }

    catch (System.IO.FileNotFoundException ex)
    {
        // Put the more specific exception first.
        System.Console.WriteLine(ex.ToString());  
    }

    catch (System.IO.IOException ex)
    {
        // Put the less specific exception last.
        System.Console.WriteLine(ex.ToString());  
    }
    finally 
    {
        sw.Close();
    }

    System.Console.WriteLine("Done"); 
}


21. catch 블록을 실행하기 전에 런타임은 finally 블록을 확인합니다.

22. Finally 블록을 사용하면 취소된 try 블록에서 남겨질 수 있는 모호한 상태를 모두 정리할 수 있고 런타임에 가비지 수집기가 개체를 종결하기를 기다리지 않고도 그래픽 핸들, 데이터베이스 연결, 파일 스트림 등의 외부 리소스를 해제할 수 있습니다.

23. 예를 들면 다음과 같습니다.


static void TestFinally()

{
    System.IO.FileStream file = null;
    //Change the path to something that works on your machine.
    System.IO.FileInfo fileInfo = new System.IO.FileInfo(@"C:\file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        if (file != null)
        {
            file.Close();
        }
    }

    try
    {
        file = fileInfo.OpenWrite();
        System.Console.WriteLine("OpenWrite() succeeded");
    }
    catch (System.IO.IOException)
    {
        System.Console.WriteLine("OpenWrite() failed");
    }
}


24. WriteByte()에서 예외를 throw한 경우 file.Close()를 호출하지 않으면 파일을 다시 열려고 하는 두 번째 try 블록의 코드가 실패하고 해당 파일은 잠금 상태로 유지됩니다.

25. finally 블록은 예외가 throw되는지 여부와 상관없이 실행되므로 위 예제에서 finally 블록을 사용하면 파일을 올바르게 닫고 오류를 방지할 수 있습니다.

26. 예외가 throw된 후에 호출 스택에서 호환되는 catch 블록을 찾지 못하면 다음 세 가지 결과 중 하나가 발생합니다.

1) 예외가 소멸자 내에 있는 경우에는 소멸자가 취소되고 기본 생성자가 있으면 이 생성자가 호출됩니다.

2) 호출 스택에 정적 생성자 또는 정적 필드 이니셜라이저가 있는 경우에는 원래 예외가 새 예외의 InnerException 속성에 할당된 상태로 TypeInitializationException이 throw됩니다.

3) 스레드의 시작 부분에 도달하면 스레드가 종료됩니다.




예외 처리


1. try 블록은 C# 프로그래머가 예외의 영향을 받을 수 있는 코드를 분할하는 데 사용됩니다.

2. 관련 catch 블록은 예외 결과를 처리하는 데 사용됩니다.

3. finally 블록에는 try 블록에 할당된 리소스의 해제 같이 try 블록에서 예외가 throw되는지 여부에 관계 없이 실행되는 코드가 포함되어 있습니다.

4. try 블록에서는 하나 이상의 관련 catch 블록이나 finally 블록, 또는 두 블록 모두를 필요로 합니다.

5. 다음 예제는 try-catch 문, try-finally 문 및 try-catch-finally 문을 보여 줍니다.


try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}


try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}


try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks 
    // goes here.
}


catch 또는 finally 블록이 없는 try 블록은 컴파일러 오류가 발생합니다.


catch 블록

1. catch 블록에서는 catch할 예외 형식을 지정할 수 있습니다.

2. 형식 지정을 예외 필터라고 합니다.

3. 예외 형식은 Exception에서 파생되어야 합니다.

4. 일반적으로, try 블록에서 throw될 수 있는 모든 예외를 처리하는 방법을 명확히 알고 있거나 catch 블록의 끝에 throw 문을 포함하는 경우가 아니라면 Exception을 예외 필터로 지정해서는 안 됩니다.

5. 예외 필터가 서로 다른 여러 개의 catch 블록을 함께 연결할 수 있습니다.

6. catch 블록이 코드 내 위에서 아래 순으로 계산되지만 throw된 각 예외에 대해서는 catch 블록이 하나만 실행됩니다.

7. throw된 예외의 정확한 형식이나 기본 클래스를 지정하는 첫 번째 catch 블록이 실행됩니다.

8. 일치하는 예외 필터를 지정하는 catch 블록이 없는 경우 문에 블록이 표시된 경우 필터가 없는 catch 블록이 선택됩니다.

9. catch 블록은 가장 구체적인, 즉 가장 많이 파생되는 예외 형식에 먼저 배치해야 합니다.

10. 다음 조건에 해당되면 예외를 catch해야 합니다.

1) 이제 예외가 throw될 수 있는 이유에 대해 올바르게 이해했으며 FileNotFoundException 개체를 catch할 때 새 파일 이름을 입력하라는 메시지가 표시되는 경우와 같이 상황에 맞게 적절한 복구를 구현할 수 있습니다.

2) 더 구체적인 새 예외를 만들고 throw할 수 있는 경우입니다.


int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch(System.IndexOutOfRangeException e)
    {
        throw new System.ArgumentOutOfRangeException(
            "Parameter index is out of range.");
    }
}


3) 추가 처리를 위해 전달하기 전에 예외를 부분적으로 처리합니다. 다음 예제에서 catch 블록은 예제를 다시 throw하기 전에 오류 로그에 항목을 추가하는 데 사용됩니다.


try
{
    // Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
    // Call a custom error logging procedure.
    LogError(e);
    // Re-throw the error.
    throw;     
}


finally 블록

1. finally 블록에서는 try 블록에서 수행되는 작업을 정리할 수 있습니다.

2. finally 블록이 있는 경우 이 블록은 try 및 일치하는 모든 catch 블록을 실행한 후에 마지막으로 실행됩니다.

3. finally 블록은 예외가 throw되었는지 여부나 예외 형식이 일치하는 catch 블록을 찾았는지 여부와 상관없이 항상 실행됩니다.

4. finally 블록을 사용하면 런타임에 가비지 수집기가 개체를 종료할 때까지 기다리지 않고도 파일 스트림, 데이터베이스 연결, 그래픽 핸들 등의 리소스를 해제할 수 있습니다.

5. 자세한 내용은 using 문(C# 참조)를 참조하십시오.

6. 다음 예제에서 finally 블록은 try 블록에 열려 있는 파일을 닫는 데 사용됩니다.

7. 파일을 닫기 전에 파일 핸들의 상태를 검사합니다.

8. try 블록에서 파일을 열 수 없는 경우 파일 핸들에는 null 값이 여전히 있으며 finally 블록을 닫기 위한 시도를 하지 않습니다.

9. 또는 try 블록에서 파일이 성공적으로 열린 경우 finally 블록은 열린 파일을 닫습니다.


System.IO.FileStream file = null;
System.IO.FileInfo fileinfo = new System.IO.FileInfo("C:\\file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    if (file != null)
    {
        file.Close();
    }
}




예외 만들기 및 Throw


1. 예외는 프로그램을 실행하는 동안 오류가 발생한 경우 이를 알리는 데 사용됩니다.

2. 오류를 설명하는 예외 개체가 작성된 다음 throw 키워드를 사용하여 throw됩니다.

3. 런타임에서는 가장 적합한 예외 처리기를 검색합니다.

4. 프로그래머는 다음과 같은 조건 중 하나 이상이 true일 때 예외를 throw해야 합니다.

1) 메서드의 정의된 기능을 완료할 수 없는 경우.

2) 메서드의 매개 변수 값이 잘못된 경우를 예로 들 수 있습니다.


static void CopyObject(SampleClass original)
{
    if (original == null)
    {
        throw new System.ArgumentException("Parameter cannot be null", "original");
    }

}


3) 개체 상태를 기준으로 개체에 대한 적절하지 않은 호출을 수행한 경우.

읽기 전용 파일에 쓰려고 하는 경우를 예로 들 수 있습니다. 개체 상태 때문에 작업을 수행할 수 없는 경우 InvalidOperationException의 인스턴스나 이 클래스의 파생을 기반으로 한 개체가 throw됩니다. 다음은 InvalidOperationException 개체를 throw하는 메서드의 예제입니다.


class ProgramLog
{
    System.IO.FileStream logFile = null;
    void OpenLog(System.IO.FileInfo fileName, System.IO.FileMode mode) {}

    void WriteLog()
    {
        if (!this.logFile.CanWrite)
        {
            throw new System.InvalidOperationException("Logfile cannot be read-only");
        }
        // Else write data to the log and return.
    }
}


4) 메서드에 대한 인수로 인해 예외가 발생하는 경우.

이 경우 원래 예외를 catch하고 ArgumentException 인스턴스를 만들어야 합니다. 원래 예외를 ArgumentException의 생성자에 InnerException 매개 변수로 전달해야 합니다.


static int GetValueFromArray(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (System.IndexOutOfRangeException ex)
    {
        System.ArgumentException argEx = new System.ArgumentException("Index is out of range", "index", ex);
        throw argEx;
    }
}


5. 예외에는 StackTrace라는 속성이 있습니다.

6. 이 문자열에는 현재 호출 스택에 포함된 메서드의 이름이 각 메서드에 대해 예외가 throw된 파일 이름 및 줄 번호와 함께 포함됩니다.

7. StackTrace 개체는 throw 문이 실행되는 지점에서 CLR(공용 언어 런타임)에 의해 자동으로 작성되므로 스택 추적을 시작해야 할 지점에서 예외가 throw됩니다.

8. 모든 예외에는 Message라는 속성이 있습니다.

9. 이 문자열에는 예외의 원인에 대한 설명을 설정해야 합니다.

10. 메시지 텍스트에는 중요한 보안 정보가 포함되지 않도록 주의해야 합니다.

11. ArgumentException에는 Message 이외에 ParamName이라는 속성도 있으며, 이 속성에는 throw할 예외를 발생시킨 인수의 이름을 설정해야 합니다.

12. 속성 setter의 경우 ParamName은 value로 설정해야 합니다.

13. 공용 메서드와 보호된 메서드 멤버는 해당 함수를 완료할 수 없을 때마다 예외를 throw해야 합니다.

14. throw되는 예외 클래스는 오류 조건에 맞는 가장 구체적인 예외여야 합니다.

15. 이러한 예외는 클래스 기능의 일부로 문서화해야 하며 원본 클래스에 대한 업데이트나 파생 클래스는 역호환성을 위해 이와 동일한 동작을 유지해야 합니다.


예외를 throw할 때 주의해야 할 사항

1. 다음은 예외를 throw할 때 주의해야 할 사항입니다.

1) 예외는 일반적인 실행의 일부로 프로그램의 흐름을 변경하는 데 사용하면 안 됩니다. 예외는 오류를 보고하고 처리하는 용도로만 사용해야 합니다.

2) 예외는 반환 값이나 매개 변수로 반환하지 말고 반드시 throw해야 합니다.

3) 사용자가 직접 작성한 코드에서 System.Exception, System.SystemException, System.NullReferenceException 또는 System.IndexOutOfRangeException을 고의적으로 throw하면 안 됩니다.

4) 릴리스 모드가 아니라 디버그 모드에서 throw될 수 있는 예외는 만들면 안 됩니다. 개발 단계에서 런타임 오류를 식별하려면 Debug Assert를 대신 사용하십시오.


예외 클래스 정의

1. 프로그램에서는 앞서 언급한 경우를 제외하고 System 네임스페이스에 미리 정의된 예외 클래스를 throw하거나 Exception에서 파생된 고유한 예외 클래스를 만들 수 있습니다.

2. 파생 클래스에서는 기본 생성자, 메시지 속성을 설정하는 생성자, Message 및 InnerException 속성을 모두 설정하는 생성자 등 적어도 네 개의 생성자를 정의해야 합니다.

3. 네 번째 생성자는 예외를 serialize하는 데 사용됩니다.

4. 새 예외 클래스는 serialize 가능해야 합니다.

5. 예를 들면 다음과 같습니다.


[Serializable()]
public class InvalidDepartmentException : System.Exception
{
    public InvalidDepartmentException() : base() { }
    public InvalidDepartmentException(string message) : base(message) { }
    public InvalidDepartmentException(string message, System.Exception inner) : base(message, inner) { }

    // A constructor is needed for serialization when an
    // exception propagates from a remoting server to the client. 
    protected InvalidDepartmentException(System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context) { }
}


6. 새 속성은 예외를 해결하는 데 유용한 데이터를 제공하는 경우 예외 클래스에만 추가해야 합니다.

7. 새 속성을 파생된 예외 클래스에 추가하면 추가된 정보를 반환하도록 ToString()을 재정의해야 합니다.



컴파일러 생성 예외


1. 기본 작업이 실패하면 몇 가지 예외가 .NET Framework의 CLR(공용 언어 런타임)에서 자동으로 throw됩니다.

2. 다음 표에서는 이러한 예외와 오류 조건을 보여 줍니다.


Exception

설명

ArithmeticException

DivideByZeroException 및 OverflowException 등의 산술 연산 과정에서 발생하는 예외에 대한 기본 클래스

ArrayTypeMismatchException

저장된 요소의 실제 형식이 배열의 실제 형식과 호환되지 않기 때문에 배열에서 해당 요소를 저장하지 못하는 경우

DivideByZeroException

정수 계열 값을 0으로 나누려고 한 경우

IndexOutOfRangeException

배열을 인덱싱하려 할 때 인덱스가 0보다 작거나 배열 경계를 벗어난 경우

InvalidCastException

런타임에 기본 형식에서 인터페이스나 파생 형식으로의 명시적 변환이 실패한 경우

NullReferenceException

값이 null인 개체를 참조할 경우

OutOfMemoryException

new 연산자를 사용하여 메모리를 할당하는 데 실패한 경우. 이는 공용 언어 런타임에 사용할 수 있는 메모리가 부족하다는 것을 의미합니다.

OverflowException

checked 컨텍스트에서 산술 연산이 오버플로된 경우

StackOverflowException

보류된 메서드 호출이 너무 많아서 실행 스택이 부족한 경우. 일반적으로 너무 깊은 재귀 호출이나 무한 재귀 호출을 나타냅니다.

TypeInitializationException

정적 생성자가 예외를 throw했지만 이를 catch할 catch 절이 없는 경우



방법: try/catch를 사용하여 예외 처리


1. try-catch 블록의 사용 목적은 작업 코드에서 발생하는 예외를 catch하여 처리하는 것입니다.

2. 일부 예외는 catch 블록에서 처리할 수 있고 예외를 다시 throw할 필요 없이 문제가 해결됩니다.

3. 그러나 대부분의 경우에는 적절한 예외가 throw되었는지 확인하는 작업만 할 수 있습니다.


예제

1. 다음 예제에서 IndexOutOfRangeException은 가장 적절한 예외가 아닙니다.

2. 이 오류는 호출자가 전달한 index 인수 때문에 발생한 것이므로 해당 메서드에는 ArgumentOutOfRangeException이 보다 적절합니다.


class TestTryCatch
{
    static int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (System.IndexOutOfRangeException e)  // CS0168
        {
            System.Console.WriteLine(e.Message);
            // Set IndexOutOfRangeException to the new exception's InnerException.
            throw new System.ArgumentOutOfRangeException("index parameter is out of range.", e);
        }
    }
}


설명

1. 예외가 발생한 코드는 try 블록으로 묶여 있습니다.

2. 예외가 발생할 때 IndexOutOfRangeException을 처리하기 위한 catch 문이 바로 다음에 추가되어 있습니다.

3. catch 블록에서는 IndexOutOfRangeException을 처리하고 보다 적합한 ArgumentOutOfRangeException 예외를 대신 throw합니다.

4. 호출자에 가능한 한 많은 정보를 제공하려면 원래 예외를 새 예외의 InnerException으로 지정하는 것이 좋습니다.

5. InnerException 속성은 읽기 전용이므로 새 예외의 생성자에서 이 속성을 할당해야 합니다.




방법: finally를 사용하여 정리 코드 실행


1. finally 문의 목적은 예외가 throw되더라도 개체에 필요한 정리 작업을 즉시 수행하는 데 있습니다.

2. 이러한 개체는 일반적으로 외부 리소스를 사용하는 개체입니다.

3. 이러한 정리 작업의 예로는 다음과 같이 공용 언어 런타임에 의해 개체가 가비지 수집되기를 기다리는 대신 사용 직후에 FileStream에 대해 Close를 호출하는 경우를 들 수 있습니다.


static void CodeWithoutCleanup()
{
    System.IO.FileStream file = null;
    System.IO.FileInfo fileInfo = new System.IO.FileInfo("C:\\file.txt");

    file = fileInfo.OpenWrite();
    file.WriteByte(0xF);

    file.Close();
}


예제

1. 위 코드를 try-catch-finally 문으로 변환하려면 다음과 같이 정리 코드를 작업 코드와 분리해야 합니다.


static void CodeWithCleanup()
{
    System.IO.FileStream file = null;
    System.IO.FileInfo fileInfo = null;

    try
    {
        fileInfo = new System.IO.FileInfo("C:\\file.txt");

        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    catch(System.UnauthorizedAccessException e)
    {
        System.Console.WriteLine(e.Message);
    }
    finally
    {
        if (file != null)
        {
            file.Close();
        }
    }
}


1. OpenWrite() 호출 이전에 try 블록 내에서 언제든지 예외가 발생할 수 있고 OpenWrite() 호출 자체가 실패할 수도 있으므로 파일을 닫으려고 할 때 해당 파일이 열려 있으리라는 보장이 없습니다.

2. finally 블록은 사용자가 Close 메서드를 호출하기 전에 FileStream 개체가 null이 아닌지 확인하기 위한 검사를 추가합니다.

3. null 검사를 수행하지 않으면 finally 블록에서 자체적으로 NullReferenceException을 throw할 수 있습니다.

4. finally 블록에서 예외가 throw되는 경우는 가능한 한 피해야 합니다.

5. finally 블록에서 닫을 수 있는 또 다른 후보 중 하나로는 데이터베이스 연결이 있습니다.

6. 데이터베이스 서버에 대해 허용된 연결의 수는 제한될 수 있으므로 가능한 한 빨리 데이터베이스 연결을 닫아야 합니다.

7. 연결을 닫기 전에 예외가 throw되면 이 경우에도 가비지 수집을 기다리는 것보다 finally 블록을 사용하는 것이 좋습니다.




방법: CLS 규격이 아닌 예외 catch


1. C++/CLI를 비롯한 일부 .NET 언어에서는 개체가 Exception에서 파생되지 않은 예외를 throw할 수 있습니다.

2. 이러한 예외를 CLS 규격이 아닌 예외 또는 예외가 아닌 항목이라고 합니다.

3. Visual C#에서는 CLS 규격이 아닌 예외를 throw할 수 없지만 다음 두 가지 방법으로 이러한 예외를 catch할 수 있습니다.

1) catch (Exception e) 블록 내에서 RuntimeWrappedException으로 catch

기본적으로 Visual C# 어셈블리에서는 CLS 규격이 아닌 예외를 래핑된 예외로 catch합니다. WrappedException 속성을 통해 액세스할 수 있는 원래 예외에 액세스해야 하는 경우 이 메서드를 사용합니다. 이 항목의 뒷부분에 있는 절차에서는 이 방법으로 예외를 catch하는 방법을 설명합니다.

2) catch (Exception) 또는 catch (Exception e) 블록 다음에 있는 일반적인 catch 블록(예외 형식이 지정되지 않은 catch 블록) 내에서 catch

CLS 규격이 아닌 예외에 대한 응답으로 로그 파일에 쓰기 등의 일부 작업을 수행하려고 하며 예외 정보에 액세스할 필요가 없는 경우 이 메서드를 사용합니다. 기본적으로 공용 언어 런타임에서는 모든 예외를 래핑합니다. 이 동작을 사용하지 않으려면 코드(대개 AssemblyInfo.cs 파일의 코드)에 [assembly: RuntimeCompatibilityAttribute(WrapNonExceptionThrows = false)]와 같이 이 어셈블리 수준의 특성을 추가합니다.


CLS 규격이 아닌 예외를 catch하려면

1. catch(Exception e) block 내에서 as 키워드를 사용하여 e를 RuntimeWrappedException으로 캐스팅할 수 있는지 테스트합니다.

2. WrappedException 속성을 통해 원래 예외에 액세스합니다.


예제

1. 다음 예제에서는 C++/CLR로 작성된 클래스 라이브러리에서 throw된 CLS 규격이 아닌 예외를 catch하는 방법을 보여 줍니다.

2. 이 예제에서 Visual C# 클라이언트 코드는 throw되는 예외 형식이 System.String이라는 것을 미리 알고 있습니다.

3. 코드에서 원래 형식에 액세스할 수 있으면 WrappedException 속성을 해당 형식으로 다시 캐스팅할 수 있습니다.


// Class library written in C++/CLR.
   ThrowNonCLS.Class1 myClass = new ThrowNonCLS.Class1();

   try
   {
    // throws gcnew System::String(
    // "I do not derive from System.Exception!");
    myClass.TestThrow(); 
   }


   catch (Exception e)
   {
    RuntimeWrappedException rwe = e as RuntimeWrappedException;
    if (rwe != null)    
    {
      String s = rwe.WrappedException as String;
      if (s != null)
      {
        Console.WriteLine(s);
      }
    }
    else
    {
       // Handle other System.Exception types.
    }
   }




'프로그래밍 > C#' 카테고리의 다른 글

DateTime, TimeSpan  (0) 2016.10.12
C# 코딩 규칙  (0) 2016.08.28
파일 시스템 및 레지스트리  (0) 2016.08.25
형식 참조 테이블  (0) 2016.08.23
클래스 및 구조체  (0) 2016.08.05
:
Posted by 지훈2