ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C# 어트리뷰트 예외처리
    .NET/C# Basic 2008. 10. 16. 16:27
    반응형

    ////////////////////   어트리뷰트 ////////////////

    C#문법은 지정자로 각 요소의 성질을 밝힌다. public private 지정자는 맴버의 공개 여부를 통제하며, const, readonly는 변경가능성을 지정하고static abstract 는 기억장소나 구현여부를 결정한다 지정자별로 의미가 정해져 있어서 문법 요소에 다양한 성질을 부여할 수 있다 문법이 지원하는 것 이외의 성질을 더 지정하고 싶을 떄는 이 절에서 소개하는 어트리뷰트를 사용한다

    어트리뷰트 컴파일러에게 코드에 대한 여분의 추가 정보를 제공하는 선언 형식이다 이렇게 추가된 정보는 컴파일 방식에 영향을 주기도 하며 공식적이고 간결한 형태로 코드를 문서화하기도 한다 어트리뷰트 형태로 작성된 기록은 실행파일에 포함되며 리플레션 기술을 사용하여 실행 중에 조사할 수도 있고 자동화된 프로그래밍 방식으로 처리할 수도 있다 사용자가 원하는 어트리뷰트를 마음대로 정의하여 붙일수 있으므로 개수나 활용 용도에 대한 제약이 없다

    클래스 필드 메서드 프로퍼티 등 프로그램을 구성하는 거의 모든요서에 어틀뷰트를 적용할수잇다 적용대상앞에 []괄호와 함께 어트리뷰트 명을 적고 어틀뷰트명 다음의()괄호 안에 인수들을 지정한다. 인수는 어트리뷰트를 초기화하는 방식을 지정하는데 일단은 메서드의 인수와 유사하다고 생각하면 된다 이름없는(unnamed, positional) 이수와 이름있는(named) 인수가 잇는데 지정하는 방법이 조금 다르다 기본형식은 다음과같다

    [SomeAttribute(인수)]
    클래스 , 메서드, 프로퍼티, 인수 등등


    어트리뷰트는 크게 시스템이 제공하는 공통 어트리뷰트 사용자가 직접정의하는 커스텀어트리뷰트로 구분된다  공통어트리뷰튼는 주로 컴파일방식에 영향을 주며 커스텀 어트리뷰트는 단순한 주석이다, 먼저 공통 어트리뷰트 몇가지를 알아보자

    Conditinal        -- using System.Diagnostics; 가 포함되어야한다

     메서드의 컴파일 여부를 조건부로 결정한다 이때 어트리뷰트 인수로 심볼을 지정하는데 이심볼이 #define으로 정의되어 있으면 해당 메서드를 컴파일하고 그렇지 않으면 무시한다
    #if 전처리문과 용도는 동일하되 메서드 정의부뿐만 아니라 호출부까지 한꺼번에 조건부 처리한다는 점이 다르다. 정의부에만 Conditinal어트리뷰트를 작성해 놓으면 호출부는 따로 처리하지 않아다된다

    #if 는 기계적으로 소스를 재구성하는데 비해 Conditinal어트리뷰트는 논리적으로 컴파일 및 호출 여부를 같이 결정한다는 점이 다르다. 이 어트리뷰트는 오로지 메서드에만 적용할 수 있으며 클래스나 필드 등에는 적용할 수 없다 그것도 아무 메서드에나 다 적용할 수 있는 것은 아니고 몇 가지 조건이 있다

    1. 인터페이스의 메서드 선언문에는 쓸 수 없으며 인터페이스를 구현하는 메서드에서 쓸 수 없다
    인터페이스는 계약이기 때문에 처음 정할때부터 확정적이어야 하며 조건에따라 계약이 바뀌어서는 안된다 그현하는 쪽에서도 계약대로 무조건 메서드를 구현해야지 조건부로 메서드의 구현여부를 결정할 수는 없다
    2. 가상메서드에서 쓸수 있지만 재정의되는 메서드에는 쓸수 없다. 부모의 가상메서드가 조건부로 컴파일되면 이 메서드를 상속받는 자식 클래스에서도 조건의 영향을 받는다. 재정의되는 메서드는 어트리뷰트에 상관없이 자동으로 조건부이며 따라서 별도의 Conditinal 어트리뷰트를 붙일 필요가 없다
    3. 조건에 따라 호출될 수도 있고 아닐 수도 있으므로 이 메서드가 없더라도 다른 부분이 영향을 받아서는 안된다. 조건부 메서드는 있어도 그만 없어도 그만인 그런 메서드여야 하며 실행에 중대한 역할을 하는 메서드는 조건부가 될 수없. 그래서 리턴타입은 반드시 void 여야 하며 출력용 인수가 있어서도 안된다 조건부로 컴파일되는 메서드에 의존하는 다른코드가 있다면 이코드들도 모두 조건부 처리되어야 논리적으로 문제가 없다

    다음 예제는 셰어웨어 프로그램의 한 얘인데 TRAIL 심볼의 정의 여부를 따라 TrialMessage 메서드의 컴파일 여부가 결정된다

    #define TRIAL
    using System;
    using System.Diagnostics;
    class CSTEST
    {
     [Conditional("TRIAL"), Conditional("Free")]
     static void TrialMessage()
     {
      Console.WriteLine("이제품은 30일 간만 사용할 수 있습니다");
      Console.WriteLine("정품을 구입하시려면 어쩌고할래?");
      Console.WriteLine("정품을 등록하시려면 ssf@sex.man");
     }
     [Conditional("FREE")]
     static void TrailMessage2()
     {
      Console.WriteLine("Free");
     }
     static void Main()
     {
      TrialMessage();
      Console.WriteLine("게임을 시작합니다");
     }
    }

    TrialMessage메서드 앞에 Conditional 어트리뷰트를 붙여 TRIAL 심볼이 정의되어 있을 때만 컴파일하도록 했다.
    이대로 컴파일하여 실행하면 TrialMessage도 같이 컴파일되며 프로그램시작전에 이메서드를 호출하여 공짜 사용자를 귀찮게 하기위한 메세지가 출력된다

    소스선두의 #define TRIAL을 주석 처리하여 이 심볼을 제거하며 TrialMessge 메서는 컴파일 대상에서 제외되며 이 메서드를 호출하는 코드도 같이 제거 된다 TRIAL 심볼을 없앤 후 다시 컴파일하며 메시지가 출력되지 않고 곧바로 게임이 시작된다. 두개의 조건을 같이 기술하면 둘 중하나라도 정의되어 있을 때 컴파일된다

    [Conditinal("TRIAL"), Conditional("FREE")]
    static void TrialMessage() {.....}

    이렇게 하면 TRIAL과 FREE 둘다 정의되어 있을 때만 메시지가 출력되며 둘중 하나라도 정의되어 있지 않으면 메시지는 출력되지 않는다. 조건부 컴파일 기능은 여러 가지 활용도를 가지고 있는데 특히 디버그 버전과 릴리즈 버전을 다르게 작성해야 할 떄 실용적이다. 디버그 버전일 떄는 DEBUG라는 심볼이 정의되므로 이 심볼이 있을 떄 트레이스 출력문이나 로그 기록문을 작성하여 디버깅 용도로 쓸 수 있다.

    Obsolete            -- using System.Diagnostics; 가 포함되어야한다


    Obsolete 어트리뷰트는 곧 폐기될 구식 코드임을 표시한다

    [Obsolete("메시지",error)]
    대상


     두개의 인수를 취하는데 첫 번째 인수는 경고나 오류 메시지 문자열이다. 여기서 지정한 문자열이 컴파일러의 출력 창에 나타난다. 어떤 이유로 이 요소를 폐기하는지, 대체 수단은 무엇인지, 언제쯤없어지는지 등을 상세하게 기술할 수 있다. 두번째 인수는 오류 발생여부인데 이인수가 true이면 에러로 처리되어 컴파일이 거부된다false이거나 생략하면 경고로 처리되는데 경고는 단순한 알림 서비스일 뿐이므로 당분간은 꼐속 쓸 수 있다


    using System;
    using System.Diagnostics;
    class CStest
    {
     [Obsolete("NewMethod를 사용하시오",false)]
     static public void OldMethod() 
     {
      Console.WriteLine("OldMethod");
     }
     static public void NewMethod()
     {
      Console.WriteLine("새로 만든 최신형 메서드입니다");
     }
     [Obsolete("앞으로는 이 메서드를 쓰지 마시오", true)]
     static public void OutDateMethod() {}
     static void Main()
     {
      OldMethod();
      NewMethod();
      //OutDateMethod();
     }
    }


    DllImport

    DllImport 어트리뷰트는 외부 DLL의 함수를 선언할 때 사용한다.
    COM 라이브러리의 함수나 Win32 API 함수를 사용하고 싶을  때 이 어트리뷰트로 어떤 DLL에 있는 함수인지를 밝힌다. 다음예제는 두개의 API 함수를 호출한다

    using System;
    using System.Runtime.InteropServices;
    class CStest
    {
     [DllImport("User32.dll")]
     public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
     [DllImport("Kernel32.dll")]
     public static extern uint WinExec(string Path, uint nCmdShow);
     static void Main()
     {
      MessageBox(0,"메모장을 실행합니다","알림",0);
      WinExec("notepad.exe",1);
     }
    }





















    //////////////// 예외 처리 //////////////////


    using System;
    class CStest
    {
     static int[] ar = {1,2,3,4,5 };
     static int idx = 8;
     static public void Method1()
     {
      Method2();
     }
     static public void Method2()
     {
      Console.WriteLine(ar[idx]);
     }
     static void Main()
     {
      try
      {
       Method1();
      }
      catch(Exception e)
      {
       Console.WriteLine(e.Message);
      }
     }
    }


    Main에서 Method1을 호출하고Method1은 Method2를 호출한다 Method2에서 예외가 발생할수도
    있는데 이럴때는 메서드를 최초 호출한 곳으로 돌아가야한다. 예외 처리구문에서는 메서드
    내부에서 예외 발생시 자신을 포함하고 있는 가장 가까운 try 블록을 찾아 이블록에 대응되는 catch를
    샐행한다. 단계가 아무리 깊어도 잘 동작한다


    예외처리 구문은 컴파일러가 이전 단계를 찾아 점프하도록 코드를 생성하므로 단계가 아무리 깊어도 상관없다. 또한 중간 단계의 메서드에서 생성한 지역 변수들도 모두 자동으로 해제되는데 이 기능을
    스택 풀기라고 한다. 최초 호출원에서 예외를 받아 처리했을때는 메서드를 호출하기 전의 상태로 스택이 그대로 복구 되어 다음 명령을 정상적으로 실행할 수 있다














    using System;

    class MainApp
    {
    public static void Main()
    {
    Console.WriteLine("어느 예외를 발생시키겠습니까?");
    Console.WriteLine("(1)IndexOutOfRangeException ");
    Console.WriteLine("(2)Exception");

    string s = Console.ReadLine();

    if (s == "1")
    raiseErr(1);
    else
    raiseErr(2);
    }

    public static void raiseErr(int n)
    {
    try
    {
    if (n == 1)
    {
    int[] j = new int[1];
    j[3] = 2;
    }
    else
    throw (new Exception());
    }
    catch (IndexOutOfRangeException e) // 먼저 실행해서 오류잡고 안되면 밑으로
    {
    Console.WriteLine("{0} IndexOutOfRangeException 이 예외를 잡았습니다.", e.GetType());
    }
    catch (Exception e) //여기서 다 잡아
    {
    Console.WriteLine("{0} excetion 이 예외를 잡았습니다.", e.GetType());
    }
    }
    }





     











    반응형

    댓글

Designed by Tistory.