레이블이 C#인 게시물을 표시합니다. 모든 게시물 표시
레이블이 C#인 게시물을 표시합니다. 모든 게시물 표시

2012년 10월 10일 수요일

Marshaling LPCTSTR


C# (Managed code) 에서 C++ DLL (Unmanaged code) 의 다음과 같은 함수를 사용해야 할 경우가 있었다.

C++
void Func(LPCTSTR szKey);

C/C++ 기억이 가물가물 하지만.. LPCTSTR 타입은 다음과 같다.
UNICODE 을 정의할 경우 -> LPCWSTR (16-bit Unicode character) 즉, CONST WCHAR *
ANSI 일 경우 -> LPCSTR (8-bit ANSI character) 즉, CONST CHAR *

*) Windows API 는 ANSI string 과 Unicode (UTF-16) string 을 사용한다.
*) C# 은 Unicode (UTF-16) 을 사용한다.

LPCTSTR 의 대응되는 C# 타입은 [UnmanagedType.LPWStr] string 이다.

C++              C#
LPCTSTR -> [UnmanagedType.LPTStr] string


UnmanagedType 열거형
매개변수나 필드를 Unmanaged 코드로 어떻게 마샬링 할 것인지 지정하는 열거타입

UnmanagedType.LPWSTR
2바이트 null 로 끝나는 유니코드 문자열. Unmanaged 함수인 CoTaskMemAlloc 로 생성된 문자열에 대해서만 사용됨.
ANSI 문자열로 마샬링할 경우에는 UnmanagedType.LPSTR, Unicode 문자열로 마샬링할 경우에는 UnmanagedType.LPWSTR 을 사용하는 것 같다. 플랫폼 종속적인 경우에는 UnmanagedType.LPTSTR 을 사용해되 괜찮을것 같다.

MarshalAsAttribute
Managed 코드와 Unmanaged 코드 사이에 데이터를 어떻게 마샬링 할 것인지 지정한다. parameter, field, return value에 사용 가능하다.



*) Windows Data Types - MSDN
*) MarshalAsAttribute Class - MSDN
*) UnmanagedType 열거형 - MSDN
*) How to map Win32 types to C# types when using P/Invoke? - stackoverflow


@

2012년 9월 16일 일요일

How to open a WOW64 registry key


 응용프로그램의 플랫폼을 x86 으로 설정하였다. 이 응용프로그램은 x86 환경에선 당연히 x86 응용프로그램으로 동작하고, x64 환경에선 x86 으로 WOW로 동작한다. 응용프로그램에서 다음과 같이 레지스트리 키를 생성한다면?!

string portRegPath = "Software\\Yuk\\MyApp\\RegDemo";
key = Registry.LocalMachine.OpenSubKey(portRegPath, true);
if (key == null)
{
      key = Registry.LocalMachine.CreateSubKey(portRegPath);
}

x86 플랫폼의 응용프로그램이 x86 응용프로그램으로 실행 될 경우 LocalMachiine 의 Software\\Yuk\\MyApp\\RegDemo 에 기록. x64 WOW 로 실행 될 경우 LocalMachine 의 Software\\Wow6432Node\\Yuk\\MyApp\\RegDemo 에 기록.

즉, WOW로 동작하는 응용프로그램을 위한 레지스트리가 따로 존재한다.


또한 CreateSubKey 에서 x86 레지스트리 경로를 사용하여 기록해도, x64에선 자동으로 Wow6432Node 레지스트리 경로에 기록된다. (레지스트리를 읽을 때도 동일하게 적용됨.)
*) x86 플랫폼으로 빌드된 응용프로그램일 경우만 그러하고, Any CPU, Mixed Ploatforms 로 빌드된 응용프로그램은 레지스트리 경로를 각각 직접 지정해줘야 한다.

직접적으로 WOW64 레지스트리 경로를 얻어야 할 경우, 다음 코드를 참조

public RegistryKey GetSoftwareRoot()
{
    var path = 8 == IntPtr.Size ? @"Software\Wow6432Node" : @"Software";
    return Registry.CurrentUser.OpenSubKey(path);
}

IntPtr 은 포인터나 핸들을 나타내는 데 사용되는 플랫폼별 형식으로 IntPtr.Size 값으로 x86 에선 4, x64 에선 8 값을 갖는다.



System.Environment.Is64BitOperationSystem
.NET Framework 4.0 이상 환경에서 개발한다면 Environment.Is64BitOperationSystem 프로퍼티를 이용하면 더 직관적으로 코딩할 수 있다. Environment.Is64BitOperaionSystem 프로퍼티는 현재 운영체제가 64비트 운영체제인지 확인해 준다.
RegistryKey registryKey; if (Environment.Is64BitOperatingSystem == true) { 
    registryKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64); } else { 
    registryKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry32); } 


참조
- How to open a WOW64 registry key from a 64-bit .NET application - stackoverflow
- Checking if the Current Process is 64-bit - blackwasp
- Checking if the Operating System is 64-bit - blackwasp

@

2012년 5월 24일 목요일

Using C# DateTime in Sql query

 DB의 어떤 테이블의 컬럼 타입이 datetime 타입이 있다. 쿼리문을 문자열로 작성한 뒤 SqlDataAdapter, SqlCommand 클래스를 이용해 쿼리문을 실행시킬 경우가 있다. int 타입, byte 타입, nvarchar 타입 관련해서는 쉽게 쿼리문을 문자열로 작성할 수 있지만 컬럼 타입이 datetime 이면 생각처럼 쉽지가 않았다. 시간의 표현 범위와 다른 나라들의 시간 표현 순서도 다르고.

이 경우 SqlParameter 클래스를 이용하면 쉽게 해결 가능하다.





DateTime MyDate = new DateTime(1997, 9, 12);

SqlDataAdapter adapter = new SqlDataAdapter("select * from orders where OrderDate=@mydate",                    
"server=pedro4;database=Northwind");

SqlParameter myParam = new SqlParameter();
myParam.ParameterName = "@mydate";
myParam.SqlDbType = SqlDbType.DateTime;
myParam.Value = MyDate;
adapter.SelectCommand.Parameters.Add(myParam);

DataSet ds = new DataSet();
adapter.Fill(ds);

Console.WriteLine(ds.Tables[0].Rows.Count.ToString());
Console.ReadLine();



cf) Using C# DateTime in Sql query - Joel on Software

2012년 3월 2일 금요일

SortedList(Of TKey, TValue) Class

 어떤 자료를 보다가 SortedList 라는 데이터 타입을 보고 기록에 남겨 봅니다.
자세한 정보는 MSDN에서!!
SortedList(Of TKey, TValue) Class - MSDN


SortedList(Of TKey, TValue) 는 key/value pair 을 갖는 제네릭 클래스 배열이다.
제네릭 특성을 갖는것을 보니 .NET Framework 3.5 에 포함된 것 같다.


SortedDictionary(Of TKey, TValue) 와의 관계
유사점은 유사한 객체 모델을 갖으며, O(log n) 의 시간 복잡도를 갖는다.
차이점은 메모리 사용량 과 데이터의 삽입, 삭제 속도의 차이를 갖는다.

SortedList(Of TKey, TValue) 는 SortedDictionary(Of TKey, TValue) 보다 적은 메모리를 사용한다.
SortedDictionary(Of TKey, TValue) 는 정렬되지 않은 데이터에 대하여 빠른 삽입, 삭제 속도를 갖는다.
SortedList(Of TKey, TValue) 데이터가 모두 정렬되어 있을 경우 SortedDictionary(Of TKey, TValue) 보다 빠르다.

SortedList(Of TKey, TValue) 는 Key에 의해 정렬된 Key/Value 쌍의 배열로 구현되어 있다. KeyValuePair(Of TKey, TValue) 객체를 통해 탐색할 수 있다.

이름에는 -List 라고 붙지만 Key/Value 쌍으로 데이터 갖기 때문에 Key 값은 유일한 값이여야 한다. Key 값은 Null 이 될 수 없지만 Value 값는 Null 값을 갖을 수 있다.

SortedList(Of TKey, TValue) 는 정렬 및 비교 연산을 위해 comparer 구현을 요구한다. 기본적인 comparer인 Comparer(Of T).Default 는 키 타입인 TKey 가 System.IComparable(Of T) 를 구현 했는지를 체크한다. 구현 했다면 그것을 사용한다. 만약 TKey 가 IComparable(Of T)를 구현하지 않았다면 Comparer(Of T).Default 는 키 타입 TKey 가 System.IComparable 을 구현 했는지 체크한다. 만약 이 둘 모두 구현하지 않았다면 매개변수를 갖는 생성자를 오버로드 한 뒤, System.Collections.Generic.IComparer(Of T) 를 구현해 준다.

예제를 보니 List 계열의 데이터 관리와 Dictionary 계열의 데이터 관리가 같이 사용된다.

기본적으로!
~List 는 배열로 관리되며 빠른 검색에 유리한 계열이다
~Dictionary 는 Linked List 라고 생각하고 빈번한 데이터의 추가/삭제에 유리한 계열이라 생각하자.


@

2012년 2월 14일 화요일

DateTime Format, CultureInfo format


DateTime 타입의 인스턴스로 시간값을 가지고 있을 때 ToString() 메소드를 이용해서 시간값을 여러가지 타입으로 출력할 수 있다. 월, 요일 값들을 일일이 변환해 주지 않아도 되는 편리함이 있다. 여기서 한 가지 더! 시간값을 지역 언어 설정값에 따라 지역 월, 요일 값들을 지역화 해서 출력해 주며 또한 년, 월, 일 출력 순서 또한 알맞게 변환해서 출력해 준다. 다국어 프로그래밍에 참조하면 시간을 많이 절약할 수 있을 것 같다.

[C#]public virtual string ToString(
 string format,
 IFormatProvider provider
);

format 매개변수에 출력 포멧을 설정해주고, provider 매개변수에 지역 설정을 주면 된다. MSDN의 예제를 보면 다음과 같이 다양한 포멧으로 출력 가능하다.
/** Output.
   *
   * d :08/17/2000
   * D :Thursday, August 17, 2000
   * f :Thursday, August 17, 2000 16:32
   * F :Thursday, August 17, 2000 16:32:32
   * g :08/17/2000 16:32
   * G :08/17/2000 16:32:32
   * m :August 17
   * r :Thu, 17 Aug 2000 23:32:32 GMT
   * s :2000-08-17T16:32:32
   * t :16:32
   * T :16:32:32
   * u :2000-08-17 23:32:32Z
   * U :Thursday, August 17, 2000 23:32:32
   * y :August, 2000
   * dddd, MMMM dd yyyy :Thursday, August 17 2000
   * ddd, MMM d "'"yy :Thu, Aug 17 '00
   * dddd, MMMM dd :Thursday, August 17
   * M/yy :8/00
   * dd-MM-yy :17-08-00
   */
대단하다.

자세한 설명은 MSDN 참조 :
http://msdn.microsoft.com/en-us/library/Aa326722

만약 스페인어에 해당하는 정보로 출력하자면 Provider 매개변수에 스페인어 CultureInfo 인스턴를 넘겨주면 된다.
Label1.Text = Now.ToString("D", New System.Globalization.CultureInfo("es-ES"))

대단하다.



2012년 1월 18일 수요일

Nullable 타입의 기본사용, Boxing, Operator

Visual Studio 2008 C# Sample 을 보다가 Nullable 타입에 대한 예제가 있어 테스트 하며 정리해 보았다.
cf)
http://code.msdn.microsoft.com/Visual-Studio-2008-C-d295cdba



■ 기본적인 사용법



    class Program

    {

        static void Main(string[] args)

        {

            DisplayValue(1);

            DisplayValue(null);

        }



        // num.Value throws an InvalidOperationException if num.HasValue is false

        static void DisplayValue(int? num)

        {

            if (num.HasValue == true)

                Console.WriteLine("num = " + num);

            else

                Console.WriteLine("num = null");



            try

            {

                Console.WriteLine("value = {0}", num.Value);

            }

            catch (InvalidOperationException e)

            {

                Console.WriteLine(e.Message);

            }

        }

    }



HasValue 프로퍼티의 사용 방법과 nullable 타입이 null 값을 갖고 있을때 Value 프로퍼티를 사용하면 예외가 발생한다.


■ Boxing



    class Program

    {

        static void Main(string[] args)

        {

            // null 값을 갖는 Nullable 타입의 박싱

            int? a;

            object oa;

            

            a = null;



            // oa값에 null 값이 할당됨. boxing 일어나지 않음.

            oa = a;



            Console.WriteLine("Testing 'a' and 'boxed a' for null...");



            // Nullable 타입 변수는 null 값과 비교할 수 있다.

            if (a == null)

                Console.WriteLine("\t a == null");



            // 박스된 nullable 변수는 null 값과 비교할 수 있다.

            // nullable 값을 박싱할 때, HasValue 값이 false 값을 갖는다면

            // 참조값을 null로 설정하기 때문이다.

            if (oa == null)

                Console.WriteLine("\t oa == null");







            // Nullable 타입의 언박싱

            Console.WriteLine("Unboxing a nullable type...");

            int? b = 10;

            object ob = b;



            // 박스된 nullable 타입을 언박스 시킬 수 있다.

            int? unBoxedB = (int?)ob;

            Console.WriteLine("\t b = {0}, unBoxedB = {1}", b, unBoxedB);



            // 박스된 nullable 타입의 값이 null 이라면 언박스 된 값도 null 이 된다.

            int? unBoxedA = (int?)oa;

            if (oa == null && unBoxedA == null)

                Console.WriteLine("\t a and unBoxedA are null");







            // 박싱된 nullable 타입을 nullable이 아닌 타입에 언박싱 시킬 수 없다.

            Console.WriteLine("Attempting to unbox into non-nullable type...");



            try

            {

                int unBoxedA2 = (int)oa;

            }

            catch (Exception e)

            {

                Console.WriteLine("\t {0}", e.Message);

            }

        }

    }






■ ?? 연산자의 사용



    class Program

    {

        static void Main(string[] args)

        {

            // ?? 연산자의 사용

            // 예제에서 x의 값이 null 이 아니면 x를 리턴, null 이면 -1을 리턴한다.

            int? x = null;

            int y = x ?? -1;

            Console.WriteLine("y == " + y);





            // Nullable Int 타입의 값을 Int 타입에 저장하는 방법

            // default 키워드는 해당 타입의 기본값을 리턴한다.

            int i = GetNullableInt() ?? default(int);

            Console.WriteLine("i == " + i);





            // ?? 연산자는 참조 타입에 대해서도 사용할 수 있다.

            string s = GetStringValue();

            Console.WriteLine("s = {0}", s ?? "null");



        }



        static int? GetNullableInt()

        {

            return null;

        }



        static string GetStringValue()

        {

            return null;

        }

    }






■ Demo source




cf)
Nullable 형식 사용(C# 프로그래밍 가이드)


@


2011년 12월 20일 화요일

Writing Debug and Trace Messages


원문 : Writing Debug and Trace Messages - BlackWasp

복잡한 소프트웨어는 디버깅 및 로깅 코드로 모니터링 하기 쉽게 만들어야 한다. 정말로..
고생해 보면 알아요.


What are Debug and Trace Messages?

로그는 복잡한 소프트웨에서 사용자 인터페이스(UI)만으로 동작을 관찰하기 힘들때 사용되어 진다. 로깅 메시지는 프로그램의 문제를 파악하는데 많은 도움이 된다. .NET Framework 에서는 Debug 와 Trace 메시지를 제공해 준다.

Debug messages
System.Diagnostics 네임스페이스의 Debug 클래스 사용
Visual Studio로 디버그 모드로 디버깅 시 "지역 창" 에 출력된다.
메시지 작성을 위한 메소드는 Conditional 어트리뷰트와 DEBUG 심벌을 갖는다. 따라서 프로그램이 Release 모드로 컴파일 되었다면 호출되지 않는다. (디버그 메시지가 최종 사용자에게 보여지지 않음과 디버깅 동작으로 프로그램 성능 저하 방지를 보장한다.)

Trace messages
System.Diagnostics 네임스페이스의 Trace 클래스 사용
TRACE 심벌이 정의되었을 때 코드에 포함된다. Visual Studio 는 기본적으로 TRACE 심벌을 포함하여 빌드한다. 따라서 모듈을 debug 또는 release 모드로 빌드하건 TRACE 코드가 모듈에 포함된다.


Writing Messages

디버그 메시지를 기록하는 가장 간단한 방법으로 디버그 모드에서 프로그램이 실행중일 때 출력창에 메시지를 출력시키는 방법이 있다. Debug, Trace 클래스를 사용하기 위해선 다음 네임스페이스를 using 해준다.


using System.Diagnostics;



Debug, Trace 클래스에는 Write, WriteLine 의 Static 메소드가 존재한다. 하나의 매개변수를 받으며 어떤 타입도 사용될 수 있다. String 타입일 경우 해당 값이 출력되며, 다른 타입일 경우 ToString 메소드의 결과값이 출력된다.


Debug.Write("Application started (Debug)");
Trace.Write("Application stopped (Trace)");

Debug.WriteLine("Application started"); 




Categories

출력하려는 디버그 메시지를 카테고리화 할 수 있다. Write, WriteLine 메서드의 두 번째 매개변수를 주면 되며 이 String 타입의 값이 카테고리의 이름이 된다.


Debug.Write("Application started", "Debug");
Debug.WriteLine("Application stopped", "Debug"); 






Indentation

디버그 메시지를 읽기 쉽게 하기 위해 들여쓰기를 할 수 있다. Indent 메소드를 이용하면 들여쓰기를 하고, Unindent 메소드를 호출하면 들여쓰기를 취소한다.



Debug.WriteLine("Application started", "Application");

Debug.Indent();

Debug.WriteLine("Processing input XML", "File Import");

Debug.WriteLine("Imported 300 row(s)", "File Import");

Debug.Unindent();

Debug.WriteLine("Application stopped", "Application");






Writing Messages Conditionally

디버그 메시지를 원하는 조건일때만 출력하고 싶을 경우가 있다. if 또는 switch 조건문안에서 디비그 메시지를 기록해도 되지만 이 방법은 코드 가독성이 떨어진다.
Debug, Trace 클래스는 조건 매개변수를 갖는 Write, WriteLine 메소드를 제공한다. WriteIf, WriteLineIf 메서드가 있으며 이 메소드는 첫 번째 매개번수 값이 true 일 경우만 메시지를 기록한다.


Debug.WriteLineIf(loggingEnabled, "Application started", "Application");




Using Trace Listeners

Debug, Trace 클래스는 Listeners 프로퍼티를 갖는다. Listeners 프로퍼티는 메시지를 받는 trace listener 들을 컬렉션값으로 갖는다. 보통 출력창을 Listener로 갖는데 상황에 따라 다른 Listener에 메시지를 출력해야 될 때가 있다.

TextWriterTraceListener 라는 Listener는 메지시를 텍스트 파일에 출력한다. 생성자에서 출력할 파일을 지정하며, 파일이 존재하지 않을 경우 새로 생성한다. Listeners 프로퍼티에 TextWriterTraceListener 를 추가한 후, 응용프로그램 종료 전에 Close 메소드를 호출해줘야 한다.

아래 예는 Windows Form 응용프로그램에서의 사용 예이다.
메인 폼이 열리기 전에 TextWriterTraceListener 를 추가하고, 메인 폼이 종료된 후 Close 메소드를 호출해준다. Close 메소드 호출 시 모든 메시지가 지정한 파일에 기록된다.


Debug.Listeners.Add(new TextWriterTraceListener(@"c:\temp\debug.log"));

Application.Run(new MainForm());

Debug.Close();






@

2011년 12월 13일 화요일

Using the DebuggerDisplay Attribute


원문 : Using the DebuggerDisplay Attribute - BlackWasp


디버깅 시 string 타입과 numeric 타입의 값을 볼 수 있다. 사용자 정의 객체의 경우 인스턴스 이름에 해당하는 값에 클래스의 이름 또는 ToString() 메소드의 결과값을 출력된다. DebuggerDisplay 어트리뷰트를 이용하면 이 값들을 변경할 수 있다.


The DebuggerDisplay Attribute

프로그램을 디버그 모드로 Visual Studio 디버거 모드로 실행 시 '지역 창'에서 변수의 값을 관찰할 수 있다.
변수가 객체를 포함하고 있다면 객체를 트리 구조로 표현해준다.


지역 창에서 객체 트리를 펼치면 객체의 프로퍼티와 필드값을 볼 수 있다. 위 이미지에서 경우 MainWindow 타입의 인스턴스에 해당하는 값이 클래스 이름으로 출력된다. 만약 사용자 정의 객체가 ToString 메소드를 오버라이딩 하지 않았다면 객체의 이름이 출력되어 디버깅에 도움이 되지 않는다.

디버깅시 클래스 인스턴스를 위한 유용한 정보가 보여지기 원한다면 ToString 메소드를 오버라이딩 하면 된다.(.NET Framework 1.1 요구 사항) 하지만 ToString 메소드를 클래스가 다른 목적으로 사용한다면 이 방법은 실용적이지 못하다.

*) Effective C# 에서는 사용자 정의 클래스를 정의할 때 항상 ToString 메소드를 적절하게 오버라이딩 하라고 조언한다.

ToString 메소드를 오버라이딩 하지 않고 DebuggerDisplay 어트리뷰트를 적용하면 지역 창에 표시되는 인스턴스값을 바꿀 수 있다. DebuggingDisplay 어트리뷰트는 .NET Framework 2.0 이상에서 지원한다.

DebuggerDisplay 어트리뷰트 대상
- 클래스, 구조체 및 기타 타입에도 사용할 수 있다.

DebuggerDisplay 어트리뷰트 매개변수
- 한개의 String 타입의 매개변수를 갖는다. 이 매개변수값이 지역 창의 Value 값에 출력된다.


Adding the DebuggerDisplay Attribute to a Class

테스트를 해보자! 간단한 사용자 정의 클래스를 갖는 콘솔 어플리케이션을 만든다.
Square 클래스는 Dimension 자동 프로퍼티를 갖으며 영역값을 리턴하는 Square 메소드를 갖는다.


디버깅 모드에서 s.Dimensions = 5 코드에 위치하였을 때 지역창에는 다음과 같이 출력된다. 즉, Square 클래스의 인스턴스인 s 에 값에는 클래스 이름이 출력된다.


Adding the Attribute
DebuggerDisplay 어트리뷰트는 System.Diagnostics 네임스페이스에 있으므로 네임스페이스를 using 해준다.


DebuggerDisplay 어트리뷰트 매개변수에는 출력할 값을 {} 안에 넣어준다. 프로퍼티, 메소드의 리턴값 등이 사용되어 진다.


DebuggerDisplay 에 사용한 프로퍼티가 string 타입이라면 해당 값이 "" 큰따옴표에 묶여 출력되게 된다. 해당 프로퍼티 이름에 nq를 지정하면 큰따옴표 없이 출력된다.




Using Expressions
DebuggerDisplay 어트리뷰트에 간단한 식을 사용할 수 있다. 산술 연산 및 논리 연산이 가능하다.



Demo Source





@

2011년 11월 24일 목요일

C# Conditional Methods


원문 : C# Conditional Methods - Black Wasp


C# 및 .NET Framework를 이용해 만들어진 어플리케이션은 conditional method (조건부 메소드) 를 포함할 수 있다. conditional method는 지정된 컴파일러 심벌이 정의되어 있을 경우만 호출되는 특별한 메소드 이다. 이 메소드를 사용하면 tracing 및 디버깅을 위한 코드를 더 이상적?!으로 만들 수 있다.


What are Conditinal Methods?

conditional method는 Conditional 어트리뷰터와 컴파일러 심벌 이름을 갖는 메소드이다. 지정한 컴파일러 심벌이 정의되었다면 코드 실행 시 conditional method가 실행된다. 지정한 컴파일러 심벌이 정의되어 있지 않다면 컴파일된 어셈블리에 포함되지 않는다.

목적?!
대부분의 경우 tracing 및 디버깅을 위한 용도로 사용한다.
이 경우 DEBUG 심벌을 사용한다. 디버깅 모드로 컴파일 시 컴파일 옵션에 자동으로 DEBUG 심벌이 추가되므로 디버깅시 어셈블리에 포함된다.
릴리즈 모드로 컴파일 시, DEBUG 심벌을 사용한 Conditional method는 빌드된 어셈블리에서 제외된다.

#if - #endif 와의 차이
#if - #endif 와 비슷하지만 중요한 차이점이 있다.
#if - #endif 에 둘러쌓인 부분은 지정된 심벌이 정의되지 않으면 빌드에서 제외된다.
conditional method 는 항상 빌드되어 최종 어셈블리에 포함된다. 지정한 컴파일 심벌이 존재하지 않으면 해당 메소드는 최종 어셈블리에 포함은 되지만 호출은 되지 않는다.

Conditional Method의 장점, 단점
단점으로 항상 conditional method가 어셈블리에 포함되기 때문에 어셈블리의 크기가 커진다.
장점으로는 하나의 어셈블리가 프로그램과 라이브러리에서 공유될 수 있다. 즉,  conditional method를 포함하는 하나의 어셈블리가 이 어셈블리를 호출하는 프로그램에 따라 다르게 사용될 수 있다. #if - #endif 를 사용하는 코드보다 깔끔하다?!는 장점도 있다.


Conditional Attribute

conditional 어트리뷰트를 적용할 수 있는 메소드는 void 리턴값을 갖어야 한다.
conditional 어트리뷰트는 하나의 string 매개 변수를 갖는다. 이 매개 변수에는 컴파일 심벌값이 들어가야한다.

ex)
[Conditional("DEBUG")]

public static void OutputTraceInformation()

{

    Console.WriteLine("Trace information");

}



2011년 11월 21일 월요일

Debugging Using Assertions


출처 : Debugging Using Assertions - BlackWasp


What is an Assertion?

Assertion은 유용한 디버깅 툴을 제공한다. 프로그램의 실행시간에 Assertion을 수행한다. 모든 Assertion은 필수 조건, Boolean 조건을 갖으며 항상 true가 됨을 예측한다. 만약 조건이 거짓이 된다는 것은 가정이 잘못됬다는 것이고 코드에 버그가 있다는 것이다. 이 때 프로그램을 계속 진행시키는것 보다는 코드를 중지시키고 문제를 해결하는게 정신건강에 좋다.

개발하는 동안 Assertion을 활성화 시키지만 프로그램이 개발 완료되어 사용자에게 전달할 때에는 Assertion을 비활성화 시킨다. .NET 프로그램은 Release모드로 컴파일된 어셈블리는 자동으로 Assertion이 제거되어진다.

Assertion은 private 메소드의 precondition 과 postcondition을 검증하는데 사용되기도 한다.

Assertion은 단지 디버깅의 목적만으로 사용될 수 도 있다. Assert 되는 것을 프로그램의 정상적이지 않은 처리로 여겨진다. 또는 예외 처리 메소드로 사용되어진다.


Debug.Assert Method
Debug 클래스의 Assert 정적 메소드를 이용하면 C# 코드에서 쉽게 Assertion을 생성할 수 있다. Debug 클래스는 System.Diagnostics 네임스페이스에 있다.

다음은 간단한 콘솔 데모 프로그램이다.

Debug 클래스를 사용하는 클래스 파일 최 상단에 네임스페이스를 선언한다.


        using System.Diagnostics;




        public static void Main(string[] args)
        {
            string inputString;
            int minutes;


            do
            {
                Console.WriteLine("Enter a number of minutes to add.");
                inputString = Console.ReadLine();
            } while (!int.TryParse(inputString, out minutes));


            ShowTimePlusMinutes(minutes);
            Console.ReadLine();
        }



        private static void ShowTimePlusMinutes(int minutes)
        {
            Debug.Assert(minutes >= 0);


            DateTime time = DateTime.Now.AddMinutes(minutes);
            Console.WriteLine("In {0} minutes it will be {1}", minutes, time);
        }



 간단히 Debug.Assert 메소드에 Boolean 조건식을 매개변수로 전달하여 사용할 수 있다. 이 조건식이 참이면 Assert를 통과하고 거짓이면 Assert 실패 대화상자가 출력된다.
 위 예제에서 ShowTimePlusMinutes 메소드에 Assert 메소드가 있다. minutes 값이 0보다 크거나 같으면 다음 코드로 이동하고, minutes 값이 0보다 작으면 Assertion Failed 대화상자가 출력된다. 대화상자에는 Assert 한 곳에서의 stack trace 가 출력된다.



Assertion Failed 대화상자는 3개의 옵션 버튼을 갖는다.

1. Abort (중단)
: 프로그램이 즉시 중단되고 종료된다.

2. Retry (다시 시도)
: Assert 한 곳에서 부터 Debug 모드로 들어간다.

3. Ignore
: Assertion을 무시하고 프로그램을 계속 동작시킨다.


Adding Assertion Messages
위 Assertion Failed 대화상자를 보면 X 아이콘 옆이 비어있는것을 볼 수 있다. Assert 메소드의 두 번째 매개변수를 전달하면 X 아이콘 옆에 설정한 메시지가 출력된다.


Debug.Assert(minutes >= 0, "Minutes must be zero or more");





 

X 아이콘 옆에 자세한 메시지도 추가할 수 있다. Assert 메소드의 세 번째 매개변수를 이용한다.


Debug.Assert(minutes >= 0, "Minutes must be zero or more", "The number of minutes was " + minutes);






Demo Program


2011년 11월 13일 일요일

Detecting File Changes with FileSystemWatcher


FileSystemWatcher 클래스에 대해 잘 설명해 놓았다.
Detecting File Changes with FileSystemWatcher

지정한 폴더의 Changed 이벤트에 대한 활용이 어렵지만 참고할 만 하다.



2011년 6월 7일 화요일

Get IPv4 Address using Dns.GetHostEntry



 예전부터 찜찜하게 생각했던 부분을 오늘 집고 넘어가려고 한다.

로컬 호스트의 IP 주소를 얻기 위해서 Dns.Resolve 메소드를 사용했다.
간단하게 로컬 호스트의 IPv4 주소를 얻을 수 있다.

하지만 VS 출력창에 다음과 같은 경고 메시지가 나온다.




 

Dns.GetHostEntry 메소드를 사용하면 되는데 그렇게 되면 결과셋인 IPHostEntry 객체에 로컬 호스트가 사용하는 IPv4, IPv6 등 모든 정보를 갖게 된다.
나중에야 IPv6 를 사용하긴 할테지만 지금 프로젝트에선 IPv4만 사용하는데.. 이 것 만 어떻게 뽑아 낼까??

외국 아이들도 같은 고민을 했나보다. 뭐... 결국 MSDN만 잘 봤으면 쉽게 해결할 수 있는 거지만..
cf ) GetHostEntry.AddressList[0] returns ::1 on current Windows version even with IPv6 off. Can I change this?

IPHostEntry 에서 AddressList의 IPAddress 인스턴스의 AddressFamily 프로퍼티 값으로 선별 가능하다.

2011년 5월 26일 목요일

여러 스레드가 데이터 멤버 참조 volatile


 여러 스레드가 하나의 데이터 멤버를 참조하고 싶을 때 volatile 멤버 필드를 사용했다.
즉, 여러 작업을 스레드풀로써 동작시켜 놨는데, 중간에 사용자가 그 모든 동작들을 취소하고 싶은 경우이다.
취소를 명령했을 때, volatile 필드값을 수정하고 스레드풀에서 작업하던 모든 스레드들은 이 volatile 필드값을 확인하고 종료 시켰다.

volatile 키워드는 동시에 실행중인 여러 스레드에 의해 변경될 수 있음을 나타낸다.

volatile(C# 참조) - MSDN

방법: 스레드 만들기 및 종료 (C# 프로그래밍 가이드) - MSDN




2011년 5월 12일 목요일

WaitHandle을 이용한 ThreadPool 스레드 종료 대기에 대하여.


 ThreadPool FCL을 이용하여 멀티스레딩 작업을 할 때, 스레드풀에 맞긴 모든 스레드가 종료될때까지 어떻게 기다려야 하는지 고민해 보았다.

 우선 CodeProject를 뒤져 ThreadPool 의 멤버 메소드인  GetMaxThreads, GetAvailableThreads 를 이용하는 방법을 찾았다.
cf) Waiting for All Threads in theThread Pool to Finish
하지만 위 방법은 규모가 있는 프로젝트에는 적용할 수 없다. ThreadPool은 정적 클래스이기 때문에 어셈블리 여러곳에서 사용되어질 수 있기 때문이다.

 그래서 다시 찾아본 방법은 ManualResetEvent 클래스와 WaitHandle 클래스를 이용하는 방법이다. WaitHandle의 정적 메소드인 WaitAll, WaitAny를 사용하였는데, 사용하다 발견한 점은 WaitAll, WaitAny의 매개변수로 넘겨주는 ManualResetEvent 인스턴스의 개수가 64개를 넘으면 NotSupportedException 예외가 발생한다.

 하는 수 없이, ThreadPool에 스레드를 넘길때 63개 이하씩 넘기는 방법으로 사용하였다.

다른 방법이 있나요??

2011년 2월 9일 수요일

Waiting for All Threads in theThread Pool to Finish



ThreadPool을 이용할 때, ThreadPool에 위임한 백그라운드 스레드들의 작업이 모두 끝난지 어떻게 확인할 수 있을까?


MSDN에서 읽은 자료가 아니라 확신은 들지 않지만 한 블로그의 포스트를 참조하였다.






 Thread 클래스를 이용하여 직접 스레드를 생성하였을 때, 스레드의 작업 완료를 기다리기 위해 Join 메소드를 사용할 수 있다. 하지만 ThreadPool 클래스는 Join 메소드를 제공하지 않는다. ThreadPool 의 정적 메소드인 GetMaxThreads, GetAvailableThreads 메소드를 사용하여 ThreadPool의 모든 스레드가 종료될 때 까지 기다릴 수 있다. 참조한 포스트에서는 이런 방법이 충분하지 않다면 직접 ThreadPool을 구현 하라고 한다.





GetMaxThreads


스레드풀에서 허용되는 최대 작업자 스레드 수를 구한다.






GetAvailableThreads


조사하는 시점에, 스레드풀에서 시작될 수 있는 추가 작업자 스레드 수 즉, 사용 가능한 스레드 수를 구한다.






참조한 코드를 보면,








        public static void Main(string[] args)


        {


            for (int i = 0; i < 2000; i++)


            {


                Thread.Sleep(50);


                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc), i);


            }





            Console.WriteLine("Main Thread waiting to complete...");





            bool isWorking = true;


            int workerThreads = 0;


            int completionPortThread = 0;


            int maxWorkerThreads = 0;


            int maxCompletionPortThreads = 0;





            ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);





            while (isWorking)


            {


                ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThread);





                if (workerThreads == maxWorkerThreads)


                    isWorking = false;


                else


                    Thread.Sleep(500);


            }





            Console.WriteLine("Main Thread completing...");


            Console.ReadKey();


        }








        /// <summary>


        /// 스레드에서의 임의 작업, Sleep 사용


        /// </summary>


        private static void ThreadProc(object stateInfo)


        {


            Console.WriteLine("Thread {0} running...", stateInfo);


            Thread.Sleep(1000);


        }







1. QueueUserWorkItem, Sleep


스레드풀에 QueueUserWorkItem으로 작업을 넣을 때, Sleep(50) 이 있다. 스레드풀에 많은 작업을 넣을 때, 스레드풀에서 블록되는 경우가 있다. 잠깐의 Sleep을 주거나 다른 방법으로 이 문제를 해결 가능하다.





2. GetMaxThreads, GetAvailableThreads


모든 작업을 스레드풀에 요청한 후, GetMaxThreads 메소드로 스레드풀에서 사용 가능한 최대 스레드 개수를 저장해 둔다. 코드에서는 maxWorkerThreads 변수에 저장. 스레드풀에 사용 가능한 최대 스레드 개수는 CPU 마다 다르다. While 반복문에서 GetAbailableThreads 메소드를 실행하여 현재 스레드풀에서 사용 가능한 상태의 스레드 개수를 구한다. 스레드풀에서 처리할 작업을 모두 마친다면 사용 가능한 스레드수가 스레드풀의 사용 가능한 최대 스레드의 개수와 동일해질 것 이다. 그렇다면 스레드풀의 모든 스레드가 종료되었음을 추측할 수 있다. 코드에서는 if (workerThreads == maxWorkerThreads) 부분이다. 





3. Sleep


스레드풀의 모든 스레드가 종료되었는지 판별하는 while 루프에서 모든 스레드가 종료되지 않았을 때 Sleep을 주었다. 










간 혹 몇개의 작업은 잃어버리는것 같긴한데.. 잘 되는 것 같다.











* 이 방법 말고 다른 방법이 있으면 알려주세요. :)

cf) WaitHandle을 이용한 ThreadPool 스레드 종료 대기에 대하여.

2010년 11월 28일 일요일

C# Convert Int to Bool












 다른 모듈과 설정파일을 공유하는 경우가 있다. 한 예로 MFC 모듈과 .NET 모듈이 XML 파일로 설정을 공유하는데, 특정 기능의 사용, 사용 안함을 1 과 0 으로 정의하여 사용하고 있다. 설정을 읽고 체크박스 컨트롤의 상태를 결정할 때 다음과 같은 코드를 주로 사용하였다.





int useUpdate = [xml파일로 부터 설정 읽기];


ckbUseUpdate.Checked = useUpdate == 1;





 몰랐는데 int 값을 bool값으로 변환해주는 FCL이 있었다. 바로 Convert.ToBoolean


매개변수로 int 값을 받을 때, 0일 경우 false 그 외에 경우에는 모두 true 를 리턴한다.










 코딩할 때, 어쩔 수 없는 경우라면 직접 만들어야 하지만 가능하면 FCL을 이용하려 한다.





But,

프로그래밍 잘 한다고 생각하는 친구로 부터 조언을 받았다. 항상 고맙게 생각하는 친구다.

어느 언어를 막론하고 형 변환은 조심스럽게 사용해야 한다. 수 형들간의 형변환 시, Convert의 정적 메소드, 데이터 타입의 Parse, TryParse 정적 메소드를 사용하게 된다. 이 중 형 변환 시 예외를 발생시키지 않는 것은 TryParse 이다. try-catch 문을 과도하게 쓰는 코드는 좋지 않다. 따라서 TryParse 메소드의 사용이 유리한 점이 많다.




2010년 11월 20일 토요일

[Coding Style] Using C# predefined types or alias ?

C# Coding Standard by IDesign 문서를 읽고 있다. 읽다 보니까 다음과 같은 지침이 있다.

"Always use C# predefined types rather than the aliases in the System namespace"



자주 사용하는 string 타입을 예로 들면,

C# predefined type : System.String
Alias                    : string


"string" 은 System.String 의 Alias 즉 별칭이다. 예전에 읽었던 문서에서는 해당 타입의 alias가 있으면 alias를 사용하라고 했었는데 지금 읽는 문서에서는 또 그러길 비추 하고 있다. 


 딱히 어떤것이 더 유리한지 떠오르지 않아 검색해 보았다. 검색해 보니까 나와 똑같은 고민을한 흔적들이 보인다.


 자료를 읽던 중 신기한 현상을 발견했다. enum 타입을 정의할 때 요소의 데이터 타입을 지정해 줄 수 있다. 데이터 타입을 명시할 때 데이터의 전체이름(C# predefined type)을 지정해 주면 컴파일이 되지 않는다. 




또한 C# 3.5에 추가된 익명타입 var 의 사용에서도 제한이 따른다.




 어차피 코딩 스타일이라는게 개인마다 정하기 나름이지만 대부분의 경우에 있어서 alias를 사용하는것 같다. alias를 사용하면 코드의 가독성이 높아진다.  단, 메소드 이름에 데이터 타입이 들어갈때는 alias 보다는 predefined type 이름을 사용하는게 더 명확하다.



2010년 10월 8일 금요일

Exception 처리



 Effective C# 에 보면 "ITEM #44. 애플리케이션에 특화된 예외 클래스를 완벽하게 작성하라." 라는 조언?! 이 있다. 아쉽게도 일하는곳에서는 배울만한 것이 아무것도 없기에 답답해 하다가 Head First C# 을 보고 Exception 처리에 대한 기본이라 생각해 정리해 본다.








 "하나의 클래스가 예외를 발생시키면 또 다른 클래스는 예외를 잡아냅니다."





 예외를 발생시킨다는 사실 뒤에 숨어있는 핵심 요지는 무엇이 잘못될 것인가 하는 것이며, 따라서 이에 대한 비상 계획을 수립할수 있다. 일반적으로는 예외를 발생 시키고 동시에 그것을 잡아내는 메소드는 그리 흔하지 않다. 하나의 메소드에서 예외를 발생시키면 다른 객체의 또 다른 메소드에서 잡아내는 경우가 대부분이다.










즉, 위와같이 Hive 객체가 BeeProfile 객체를 생성 또는 BeeProfile의 메소드를 호출 하였을 때,  BeeProfile 에서 예외가 발생하면 BeeProfile 객체 내부에서 try-catch 로 자신의 예외를 잡아서 오로지 true, false 로 결과만 리턴해 주는게 아니다. BeeProfile 객체는 예외를 발생시켜 구체적인 예외를 던지고, 피 호출자가 해당 예외를 잡아, 각각의 예외에 맞는 처리를 해주는 것이다.











 "예외 클래스 작성"





.NET 에서 발생하는 예외 클래스들의 최상위 클래스는 Exception 클래스 이다. 따라서 사용자가 정의할 예외는 Exception 클래스를 상속 받아 자신만의 예외 클래스를 작성하면 된다.










Excpetion 클래스를 상속받아 기본 생성자만을 사용할 수 있지만, 생성자에 메시지를 인자로 갖는 생성자를 사용하여 예외가 발생한 이유를 직접적으로 알릴 수 있도록 한다.









핵심 정리!!
1. 런타임 시, 실패하는 뭔가가 발생하면 어떤 문장이라도 예외를 발생 시킬 수 있다.

2. 다양한 종류의 예외가 있는데, 각각은 Exception에서 상속받은 객체를 갖고 있다. Exception이 아닌 특정한 예외를 잡아낸다.
:  다형성에 의해 Exception 으로 모든 예외를 다 받을 수 는 있지만 발생한 각각의 예외에 따른 정확한 처리를 위해 Exception 이 아닌 해당 예외 객체로 예외를 잡고 처리해야 한다.

3. 대부분은 ArgumentException 같은 .NET 이 제공하는 예외만 발생시켜도 된다. 다른 여러 종류의 예외를 사용하는 이유는 사용자에게 각각에 대한 정보를 제공해 주기 위해서 이다. "알려지지 않은 에러가 발생했습니다." 와 같은 텍스트를 보여주는 창을 띄우는 것 보다는 "선택한 폴더가 비어있습니다. 파일이 있는 다른 폴더를 선택하세요" 를 보여주는 것이 훨씬 도움이 될 것이다.










"역사상 최악의 catch 블록 : 주석"








위 예제에서 인자로 전달받은 divisor 가 0 이면 0 으로 나누는 식이 되기 때문에 DivideByZeroException 이 발생한다. 그렇다면 이 코드가 왜 최악의 catch 블록인걸까??



묻어두는 예외




위 코드에서 0 으로 나누는 경우가 발생할 경우 try-catch로 일단 예외를 잡았기 때문에 프로그램이 비정상 종료 되지는 않는다. 하지만 0으로 나누는 경우 발생하는 예외로 인한 적극적인 처리, 알림이 없기 때문에 런타임에는 도대체 quotient가 비정상적인 값이 나오는지 알 수가 없다. 비정상 적인 값이 나온다고 일일이 디버깅 할 수도 없는 노릇이고. 더 복잡한 코드에서 이런 경우가 발생하면 한참을 디버깅 할 것이다. 퇴근은 못하겠지.. 하지만 상사는 퇴근하겠지... 

협업으로 개발하는 코드를 보면 저런 코드가 상당히 많다. 내가 뭐라할 위치가 되지 않아 가만히 두고 있는데... 상당히 안타깝다...







"일시적인 해결책도 당분간은 쓸모가 있습니다."



 예외가 발생하였을 경우, 상황에 따라 개발자가 해결책을 유도하기 보다 사용자에게 오류의 원인을 알리고, 올바른 해결책의 정보를 제공해줘야 할 때가 있다. 또한 개발자도 예외 발생 시 메시지를 발생하게 하여 해당 메시지로 예외의 원인을 파악하게 할 수 있다. 예외를 처리하는 것만큼 좋은 것은 아니지만 아무것도 하지 않는 것보다는 낫다.






"프로그램이 산산히 부서지는 것은 절대 좋지 않지만, 프로그램이 왜 충돌하고 사용자의 데이터를 가지고 무슨 일을 하는지 알지 못하는 것이 더 나쁩니다. 언제나 예상할 수 있는 에러를 처리했는지 확인하고, 처리하지 못하는 것을 기록해야 하는 이유가 바로 여기 있습니다."









예외 처리를 위한 몇 가지 간단한 팁!!

1. 에러를 우아한 방식으로 처리하는 코드를 작성하세요.

2. 사용자에게 도움이 되는 에러 메시지를 보여주세요.

3. 가급적이면 내장 .NET 예외를 던지세요. 자신만의 정보를 제공해야 할 때문 사용자 정의 예외를 발생시키세요.

4.  try블록에서는 프로그램을 중단할 것 같은 코드를 작성하세요.

5. 불필요한 파일 시스템 에러는 피하세요. 스트림을 사용할 때는 항상 using 블록을 사용하세요.









cf) Head First C#

2010년 10월 4일 월요일

Howto : Detect OS Architecture with C#

출처 : Howto : Detect OS Architecture with C#







I’ve had a problem in one of the utility applications/scripts I’ve been developing, I had to find out the Operating System Architecture, where application was running, here is the process I’ve used to do so:



   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:   
   5:  namespace SystemUtilities
   6:  {
   7:   // Enum to describe OS Architecture.
   8:   public enum OSArchitecture
   9:   {
  10:     X84,
  11:     X64
  12:   }
  13:   // Class to retrieve specific OS Variables. 
  14:   // And detect specific enviroiment conditions.
  15:   public class OSUtilities
  16:   {
  17:     // Function returns type of OS Architecture.
  18:     public OSArchitecture GetOSArchitecture()
  19:     {
  20:      if ( Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE") == "AMD64" 
  21:        || Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE") == "IA64"
  22:        || Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432") == "AMD64"
  23:        || Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432") == "IA64" )
  24:      { return OSArchitecture.X64; }
  25:   
  26:      return OSArchitecture.X84;
  27:     }
  28:   }
  29:  }


















cf) 참고





 OS Architecture 정보 즉 x86, x64 인지에 대한 정보는 레지스트리 HKLM\System\CurrentControlSet\Control\Session Manager\Environment 키의 PROCESSOR_ARCHITECTURE 값을 조사해도 알 수 있다. 또한 BCL System.Environment 정적 클래스를 이용하여 구할 수 도 있다. Environment.GetEnvironmentVariable 메소드를 이용하여 구할 수 있는데 약간 주의를 해주어야 한다. PROCESSOR_ARCHITECTURE 환경 변수값을 조사하면 OS Architecture 가 x86인 경우 "x86" 문자열 값을 구할 수 있지만 OS Architecture 가 x64 인 경우 구해지는 값은 "AMD64" 또는 "IA64" 값을 얻는다. 따라서 PROCESS_ARCHITECTURE 값이 "AMD64", "IA64" 인 경우 OS ARCHITECTURE가 x64이다. 


 Environment.GetEnvironmentVariable 의 두번째 매개변수로 타겟을 지정해 줄 수 있는데, 이 매개변수를 지정해 주지 않으면 실행시킨 프로세서를 기준으로 값이 얻어지므로 EnvironmentVariableTarget.Machine 을 이용해 주자.











C# region 에 대하여.

 조금 여락한 환경에서 .NET 개발을 하고 있다. 조금은 더 효과적, 체계적으로 배우고 싶은데..




C# 코드 파일은 C 계열 언어와 달리 .cs 파일 하나에 코드를 구현한다. 구현에 있어 클래스가 길어 질 수 있는데(클래스가 장대하게 길어지면 그것도 디자인을 잘못한듯) #region-#endregion을 이용하여 코드를 문서화 할 수 있다. 딱 여기까지는 익히 아는건데 도데체 어떻게 써야 잘 썼다고 말할까?! 고민하다 Open Source 인 log4net 은 어떻게 사용하나 살펴보았다. 


정답은 없지만 그래도 Open Source 의 코드는 경험 많은 개발자들의 코드이므로 도움이 될 것 같다.





 보통 클래스 내 멤버의 접근 지정자에 따라 분류한다. 










1. public, protected, private 등 접근 지정자 별로 분류


2. 같은 접근 지정자일 경우, 생성자, 프로퍼티, 필드 등 으로 분류


3. 가시성은 public 멤버를 제일 상단에 배치













또는 인터페이스의 구현, helper, static wrapper 와 같이 성격이 비슷한 것 끼리 묶을 수 도 있다.













#endregion 라인에 아무것도 써주지 않아도 되는데, #region-#endregion 부분이 길어질 경우 #endregion 만 써 놓으면 어떤 부분인지 찾아야 되는 번거러움이 있으므로 #endregion 옆에도 참고가 될 설명을 붙여준다.













인터페이스 구현 시 IDE가 #region 을 자동으로 추가해 준다. 구현하는 인터페이스 별로 분류하니 보기도 좋다.








 첨엔 #region-#endregion 으로 분류 해 놓으니 개발하는 중간에는 파일을 열 때 마다 #region 을 풀어주는게 귀찮았던 적이 있다. 귀찮아서 하지 않은 코드가 있었는데 나중에 열어보니... 어후...  나중을 위해 서라도 #region-#endregion 잘 활용해 보자

모를땐, 익숙하지 않을땐 따라하면서 배워야지!!