NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# 27. C#의 전처리 지시문에 대해 알아보자.

    페이지 정보

    본문

    안녕하세요. 소심비형입니다. 오늘 미세먼지 농도가 상당히 높네요. 가족이 모두 목감기에 걸려서 야외 활동하기가 좀 부담스럽군요. 환경이 빨리 개선되었으면 좋겠습니다. 아이들이 마음껏 뛰어놀 수 있어야 하는데, 우리나라 상황에서는 이마저도 쉽지 않아요. 왜 소수의 재벌을 위해 99프로의 국민이 피해를 봐야 하는지 모르겠네요. 많은 특권을 누리면 그만큼 책임도 무거워야 하는데... 한숨만 나옵니다-_-;

     

    요즘 벌어지고 있는 김제동씨 사건과 예술인 블랙리스트를 보면 어이가 없죠-_-; 정말 막장 정부가 아닌가 생각됩니다. 초딩보다도 말 못하는 실권자를 둔 우리나라가 그나마 이정도 버티는게 신기할 정도니까요.

    2NJhh1Q.jpg

     

     

    오늘은 자주 사용되지는 않지만 그래도 알아두면 유용한 전처리 지시문에 대해 알아보겠습니다. 보통 전처리기라고 표현합니다. 전처리기를 사용하면 소스 코드의 특정 부분을 조건적으로 건너 뛸 수 있도록 만들어 오류나 경고 조건을 보고하고 소스 코드의 특정 부분을 나타내도록 만들 수 있습니다. 전처리기란 용어는 C 및 C++ 프로그래밍 언어와의 일관성을 위해서만 사용합니다. C#에서는 별도의 전처리 단계가 없습니다. 전처리기는 어휘 분석 단계의 일환으로 처리됩니다.

     

    아래 예제에서는 총 3개의 전처리 지시문이 사용되고 있습니다. #define은 C 또는 C++처럼 상수를 정의하는 것과 다릅니다. C#에서는 컴파일 조건을 지정하는 역할입니다. 위에서도 언급했듯이 일관성을 위해 같은 용어를 사용하고 있을 뿐입니다. C#에서는 상수를 정의하기 위한 별도의 예약어를 제공하고 있기 때문에 필요하다면 "Constants" 클래스를 만들어서 사용해야 합니다.

    Program.cs

    #define A
    #undef B
     
    using System;
    using static System.Console;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var instance = new Z();
     
                foreach (var methodInfo in instance.GetType().GetMethods())
                {
                    if (methodInfo.DeclaringType == instance.GetType())
                        methodInfo.Invoke(instance, null);
                }
     
                ReadKey();
            }
        }
     
        public class Z
        {
    #if A
            public void A() { WriteLine("Z.A()"); }
    #else
            public void B() { WriteLine("Z.B()"); }
    #endif
     
    #if B
            public void C() { WriteLine("Z.C()"); }
    #else
            public void D() { WriteLine("Z.D()"); }
    #endif
        }
    }

     

     

    1~2라인에 컴파일 기호(define, undef)를 정의했습니다. 그리고 Z 클래스에서 #if 를 통해 조건부 컴파일을 수행하도록 하고 있습니다. 비주얼 스튜디오를 사용중이라면, 30, 34라인은 회색으로 컴파일에서 제외된다고 알려주고 있는 것을 확인할 수 있습니다. 위 코드의 결과는 아래와 같습니다.

    R1zvC8c.png

     

     

    13~19라인은 인스턴스의 맴버를 동적으로 실행하는 방법을 보여주고 있습니다. 아직 닷넷의 리플랙션을 배우지 않았기 때문에 자세하게 언급하지는 않겠지만, 위 코드의 의도는 런타임에 제외된 메소드를 보여주기 위해 동적으로 모든 맴버 메소드를 실행하도록 한 것입니다.

     

    그렇다면, #undef는 왜 사용하게 될까요? 기본적으로 비주얼 스튜디오를 통해 개발하게 되면, 자동으로 포함되는 컴파일 기호들이 있습니다. 또는 기본 설정들이 있기도 하죠. 이렇게 의도하지 않은 설정들을 피해가기 위해 undef를 사용하게 됩니다. 아래 예제를 통해 확인할 수 있습니다.

    Program.cs

    #define DEBUG
    #undef TRACE
     
    using System;
    using static System.Console;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
    #if (DEBUG)
                Console.WriteLine("Debugging is enabled.");
    #endif
     
    #if (TRACE)
                Console.WriteLine("Tracing is enabled.");
    #endif
     
                ReadKey();
            }
        }
    }

     

     

    1~2라인에 정의한 기호는 비주얼 스튜디오의 기본 설정입니다. 따라서, endif로 TRACE는 처리하지 않도록 하고 있는 것입니다. 좀 더 알아보기 위해 1~2라인을 제거하고 실행 해보세요. 그리고, 아래 그림처럼 Release모드로 변경한 후 확인해보면 각각의 옵션들이 어떻게 동작하는지 메커니즘을 쉽게 파악할 수 있습니다.

    DoP4W3s.png

     

     

    1~2라인을 삭제하고, 비주얼 스튜디오의 컴파일 옵션을 Release로 변경하고 결과를 확인 해보세요.

    V9jurHU.png

     

     

    위 예제에서 단일 조건부 지시문을 사용했는데요. 만약, 복합 조건부를 만들어야 한다면 어떻게 해야 할까요? #elif를 사용하여 처리할 수 있습니다. 일반적으로 else if를 사용하지만... 전처리기의 예약어는 예외적입니다-_-;

    Program.cs

    using System;
    using static System.Console;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
    #if VISUAL_STUDIO_2003
                Console.WriteLine("Visual Studio compiler is C# 1.");
    #elif VISUAL_STUDIO_2005
                Console.WriteLine("Visual Studio compiler is C# 2.");
    #elif VISUAL_STUDIO_2008
                Console.WriteLine("Visual Studio compiler is C# 3.");
    #elif VISUAL_STUDIO_2010
                Console.WriteLine("Visual Studio compiler is C# 4.");
    #elif VISUAL_STUDIO_2012
                Console.WriteLine("Visual Studio compiler is C# 5.");
    #elif VISUAL_STUDIO_2013
                Console.WriteLine("Visual Studio compiler is C# 6.");
    #elif VISUAL_STUDIO_2015
                Console.WriteLine("Visual Studio compiler is C# 7.");
    #endif
     
                ReadKey();
            }
        }
    }
    

     

     

     

    위 코드는 아무것도 출력하지 않습니다. 단순한 예제를 만들려다보니 위와같은 코드가 된 것 뿐입니다-_-; 아마도, 컴파일 옵션을 통해 C#의 버전에 따라 새로운 기능을 테스트하고, 결과를 확인할 수 있도록 해보려는 의도인거 같습니다. 뭐, 좀 더 코드를 보완한다면 뭔가 될거 같기도 하지만 의미없는 일처럼 느껴지는군요.

     

    아래 코드는 #warning에 대한 내용입니다. 일반적으로 조건부 지시문에 사용됩니다. 이 지시문을 사용하면 사용자에게 경고를 표시할 수 있습니다.

    ootHFql.png

     

     

    그렇다면, 이런 컴파일 경고를 활성화하거나 비활성화하려면 어떻게 해야 할까요? 아래 그림처럼 #pragma warning을 사용하면 됩니다. 만약, 쉼표로 구분되는 경고 번호를 적지 않는다면 모든 경고에 대해 적용을 받게 됩니다. 또한, 모든 경고를 활성화하려면 disable 대신 restore를 사용하세요.

    TL4jhVi.png

     

     

    #line을 사용하면 오류 및 경고에 대한 컴파일러에서 출력하는 줄 번호와 파일 이름을 수정할 수 있습니다. 이 지시문은 빌드 프로세스의 자동화된 중간 단계에서 사용합니다. 예를 들어, 원본 소스 코드 파일의 줄을 제거했지만 컴파일러에서 파일의 원래 줄 번호를 따라 출력하려는 경우, 줄을 제거한 다음 #line을 사용하여 원래 줄 번호를 시뮬레이션 할 수 있습니다.

    pFgiInA.png

     

     

    아래 예제와 같이 #line hidden을 이용하여 사이에 있는 모든 줄을 건너뛰게 만들 수 있습니다. 이 옵션을 사용해 ASP.NET에서 디버거에게 사용자 정의 코드와 시스템 생성 코드를 구별하도록 할 수 있습니다. 현재는 ASP.NET에서 이 기능을 주로 사용하지만, 앞으로는 보다 많은 소스 생성기에서 이 기능을 사용하게 될것입니다.

    6NcqQ8H.png

     

     

    디버깅 모드에서 중단점이 적중되지 않지만, 프로그램에 영향을 주진 않습니다.

    마지막으로 개발자들이 가장 많이 사용하고 있는 #region에 대해 알아보고 이 글을 마칠까 합니다. 다루지 않은 전처리기가 더 있지만, 사용이 극히 제한적이고 자주 사용되지 않기 때문에 몇가지는 건너뛰었습니다-_-; MSDN에 자세히 나와 있으니 궁금하신 분들은 참고하세요. 이 글도 MSDN을 많이 참고해서 작성한 글입니다.

     

    #region은 코드를 블록으로 감싼 후 개요 표시 및 숨기기 기능을 만들어 줍니다. 특별(?)한 Add-On을 설치하면 #region 블록의 배경색을 변경할수도 있습니다. 어떤 에드온인지 까먹었네요-_-; 보통은 코드를 정리할 때 사용하며, 중첩으로 블록화 할 수 있기 때문에 상당히 많이 사용되고 있습니다. 하지만... 너무 남용해도 별로 좋을건 없습니다. 그리고 아래 그림처럼 배너형 주석도 사용하지 않는게 좋습니다.

    qBDyqle.png

     

     

    다음 시간에...

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.