지난번 알아보았던 자동구현 프로퍼티(Auto-Implemented Properties)는 그동안 개발자들을 귀찮게 하던 멤버변수와 멤버변수에 대한 엑세스를 지원하는 프로퍼티를 생성하는데 좀 더 간결하게 표현할 수 있도록 해는 것이었다면 이번에 볼 객체와 컬렉션에 대한 이니셜라이저(Object and Collection Initializer)는 객체 또는 컬렉션의 생성을 좀더 직관적으로 할 수 있도록 해줍니다.

C# 2.0까지 객체를 생성할때 객체내의 멤버들을 동적으로 초기화 시키기 위해서는 생성자의 매개변수를 사용하거나, 생성후 객체의 각 멤버에 대해서 직접 값을 할당해주는 방법을 사용하였습니다만, C# 3.0의 Object and Collection Initializer는 생성자의 선언없이도 생성과 동시에 해당 객체나 컬렉션에 대한 초기화가 가능해지는 것입니다. 실제 코드를 보면 쉽게 이해가 가능하실 겁니다.

[코드 1-1 : C# 3.0 이전의 객체 초기화]

    1 public class Customer

    2 {

    3     private int custKey;

    4     private string name;

    5     private string address;

    6 

    7     public int CustKey { get { return custKey; } }

    8     public string Name

    9     {

   10         get { return name; }

   11         set { name = value; }

   12     }

   13     public string Address

   14     {

   15         get { return address; }

   16         set { address = value; }

   17     }

   18 }

   19 

   20 public class Program

   21 {

   22     static void Main(string[] args)

   23     {

   24         Customer customer = new Customer();

   25         customer.Name = "오성민";

   26         customer.Address = "서울시 구로구";

   27     }

   28 }

위의 코드는 언제나 보던 일반적인 코드입니다. 객체를 생성한 후 해당 객체의 멤버에 값을 할당하는 것이죠. 이 코드를 Object and Collection Initializer를 이용한 코드로 바꾼다면 아래와 같이 됩니다.

[코드 1-2 : C# 3.0 의 Object and Collection Initializer를 이용한 초기화

    1 public class Customer

    2 {

    3     public int CustKey { get; private set; }

    4     public string Name { get; set; }

    5     public string Address { get; set; }

    6 }

    7 

    8 public class Program

    9 {

   10     static void Main(string[] args)

   11     {

   12         Customer customer = new Customer

   13         {

   14             Name = "노휘겸",

   15             Address = "서울시 관악구"

   16         };

   17     }

   18 }

지난번에 보았던 자동구현 프로퍼티를 이용하여 Customer 클래스를 작성하였고, 이 클래스의 객체를 생성하는 시점에서 Customer객체의 내부 멤버들의 값을 할당하였습니다.

Object and Collection Initializer의 구성은 여러개의 멤버에 대한 이니셜라이저가 ,(콤마)로 구분되어 있으며, 이니셜라이저 외부에는 "{", "}"로 둘러싸여 구성되어 있습니다.
이니셜라이저는 접근이 가능한 멤버나 프로퍼티에 대해서만 값의 할당이 가능합니다.
즉, 위의 코드에서 본다면 set이 private 으로 구성된 CustKey의 경우에는 CustKey = 1 로 할당 할 수 없는 것입니다.
그리고, 한번에 같은 멤버를 두번이상 할당할 시에는 오류가 발생하게 됩니다.

아래는 위보다 조금 복잡한 사용예입니다.

[코드2]

    1 public class Point

    2 {

    3     public int X { get; set; }

    4     public int Y { get; set; }

    5 }

    6 

    7 public class Line

    8 {

    9     public Point P1 { get; set; }

   10     public Point P2 { get; set; }

   11 }

   12 

   13 public class Triangle

   14 {

   15     private Point p1 = new Point();

   16     private Point p2 = new Point();

   17     private Point p3 = new Point();

   18 

   19     public Point P1 { get { return p1; } set { p1 = value; } }

   20     public Point P2 { get { return p2; } set { p2 = value; } }

   21     public Point P3 { get { return p3; } set { p3 = value; } }

   22 }

   23 

   24 public class Program

   25 {

   26     static void Main(string[] args)

   27     {

   28         Line line = new Line

   29         {

   30             P1 = new Point { X = 1, Y = 1 },

   31             P2 = new Point { X = 4, Y = 4 }

   32         };

   33 

   34         Triangle triangle = new Triangle

   35         {

   36             P1 = { X = 0, Y = 0 },

   37             P2 = { X = 5, Y = 5 },

   38             P3 = { X = 5, Y = 0 }

   39         };

   40     }

   41 }

위와 같이 이니셜라이저 안에서 또다른 객체의 이니셜라이저가 존재할 수도 있으며, 이미 생성된 객체의 경우에는 값의 할당이 바로 이루어질 수도 있습니다.
line 객체의 경우 P1, P2 Point 객체가 생성되지 않았으므로 new Point {...}으로 생성과 함께 할당을 하였으나, triangle 객체의 경우에는 이미 Point 클래스의 객체들이 이미 생성되어 있으므로 생성하는 부분을 생략하고 바로 값을 할당하고 있음을 볼 수 있습니다.

만일 [코드1-2]에서와 같이 CustKey처럼 외부에서 할당이 불가능하고 생성자의 파라미터를 통해서 값을 할당해야 할때도 사용이 가능할까요?
물론 사용이 가능합니다. 생성자가 있다고 해서 달라질것은 생성자에 파라미터에 해당하는 값을 전송하면 되는 것이죠.

public class Customer

{

    public int CustKey { get; private set; }

    public string Name { get; set; }

    public string Address { get; set; }


    public Customer(int custKey)

    {

        CustKey = custKey;

    }

}


Customer customer = new Customer(1) { Name = "노휘겸", Address = "서울" };


만일 생성자에서 값이 할당된 멤버를 다시 이니셜라이저에서 값을 할당하면 이니셜라이저에서 할당한 값으로 설정이 됩니다.

이름이 Object and Collection Initializer 이므로 이제 컬렉션을 이용하는 코드를 보도록 하죠. 다들 예상하시겠지만, Collection이라고 해서 다른게 없이 위의 이니셜라이저 안에서 또 다른 이니셜라이저를 사용하는 것과 동일합니다.

    1 List<Customer> customers = new List<Customer>

    2 {

    3     new Customer(1) { Name = "노휘겸", Address = "서울" },

    4     new Customer(2) { Name = "류순필", Address = "마산" },

    5     new Customer(3) { Name = "황원준", Address = "오산" }

    6 };

Customer객체가 담기는 Generic List의 생성을 한번에 완료시켰습니다. 만일 이 코드를 C# 2.0의 코드로 작성한다면

    1 List<Customer> customers = new List<Customer>();

    2 Customer customer1 = new Customer(1);

    3 customer1.Name = "노휘겸";

    4 customer1.Address = "서울";

    5 customers.Add(customer1);

    6 ......

위와 같은 형태가 되겠죠? 보시는 여러분들이 판단하실 부분이긴 하지만, 제 주관적으로는 상당히 직관적이고 코드 읽기가 수월할 듯 하네요.

지난번 Auto-Implemented Properties 와 이번의 Object and Collection Initializer 의 경우 감탄을 자아낼 만큼 대단한 기능은 아니지만, C#의 언어의 표현력을 향상시켜주는 것 같습니다.

궁금한점이나 지적사항이 있으면 댓글로 남겨주세요.

Posted by WHiSTLE
TAG C# 3.0

트랙백 주소 http://blog.ntils.com/trackback/25 관련글 쓰기

댓글을 달아 주세요

Visual Studio의 재시작시에 CopySourceAsHtml에서 오류가 발생하는 문제의 해결방법을 첨부합니다. (2007.10.18)

블로그나 게시판등에 Visual Studio에서 개발하던 소스를 추가하기 위해서 복사&붙여넣기를 하면 탭간격이나 글꼴등의 문제로 인해서 직접 다듬어야 하는 경우가 자주 발생합니다.
개발자라면 누구나 느끼는 문제일텐데요.

Visual Studio를 이용하고 계시는 분이라면 이러한 불편함을 말끔하게 해소해 주는 Visual Studio Addin을 유경상님께서 블로그에 소개시켜주시고 있습니다.

CopySourceAsHtml 이라는 Addin인데요. 원본파일은 한글문제와 여러가지 문제가 존재하여 감사하게도 유경상님께서 따로 한글환경에서도 문제없이 돌아가는 버전까지 공개해주시고 계시죠.

좀더 자세한 내용을 알고 싶으시거나 자료를 다운로드 받고자 하시는 분은 유경상님 블로그로 방문해보세요.

(저 같은 경우에는 유경상님 블로그에서 받은 파일로 Visual Studio 2008에 등록시에 최초에는 잘 실행이 되었지만, 재 시작시에 CopySourceAsHtml에서 오류가 발생하였습니다. 이 부분의 수정과 적용에 관해서는 글 하단에서 다루도록 하겠습니다. 2007.10.18)

문제는 이 CopySourceAsHtml이 Visual Studio 2005용 Addin이라는 거죠. 물론 원본 배포사이트에는 2003버전용도 존재합니다만, 아직 Visual Studio 2008용은 배포를 하고 있지 않죠.

Visual Studio 2008이 아직 beta이긴 하지만, 이전 .NET Framework버전에 대한 프로그래밍이 가능하므로 많은 분이 사용하고 계실텐데요. 2005용 Addin을 Addins폴더로 복사만 해넣어서는 제대로 동작하질 않죠. 하지만 유용하게 하던 기능이 없으면 엄청 불편하죠..^^;

이리 저리 궁리하던차에 CopySourceAsHtml을 Visual Studio 2008에 등록시키는데 성공하여 공유차원에서 포스팅을 합니다.

  1. 우선 CopySourceAsHtml을 이용하고 계시다면 %문서%\Visual Studio 2005\Addins\ 폴더에 존재하는 CopySourceAsHtml.AddIn, CopySourceAsHtml.dll, CopySourceAsHtml.dll.config 3개 파일을 %문서%\Visual Studio 2008\Addins\ 또는 %문서%\Visual Studio Codename Orcas\Addins\ 폴더로 복사해 줍니다.(Addins 폴더가 존재하지 않는다면 만들어주면 됩니다.)
  2. 복사 되었으면 CopySourceAsHtml.AddIn 파일을 편집기로 엽니다. XML형식이므로 원하는 텍스트 편집기를 이용하시면 됩니다.
    열어보시면 <HostApplication> 이라는 태그 하위태그로 <Version>이라는 태그들의 내용을 8.0 에서 9.0으로 변경해주세요.

    <?xml version="1.0" encoding="UTF-16" standalone="no"?>

    <Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">

        <HostApplication>

            <Name>Microsoft Visual Studio Macros</Name>

            <Version>9.0</Version>

        </HostApplication>

        <HostApplication>

            <Name>Microsoft Visual Studio</Name>

            <Version>9.0</Version>

        </HostApplication>

        <Addin>

            <FriendlyName>CopySourceAsHtml</FriendlyName>

            <Description>Adds support to Microsoft Visual Studio 2005 for copying source code, syntax highlighting, and line numbers as HTML. (Modified by Loner, SimpleIsBest.NET)</Description>

            <Assembly>CopySourceAsHtml.dll</Assembly>

            <FullClassName>JTLeigh.Tools.CopySourceAsHtml.Connect</FullClassName>

            <LoadBehavior>1</LoadBehavior>

            <CommandPreload>0</CommandPreload>

            <CommandLineSafe>0</CommandLineSafe>

        </Addin>

    </Extensibility>

  3. 저장하신후 Visual Studio 2008을 실행하시고, Tools - Add-in Manager(도구 - 추가기능관리자)를 실행하고 활성화 시켜 줍니다.
     
  4. 이제 Visual Studio 2008에서도 CopySourceAsHtml을 사용하실 수 있습니다.

 

VisualStudio 시작시 오류해결방법 (2007.10.18 추가)

한번 Visual Studio를 실행하면 퇴근할때까지는 잘 종료를 안하다 보니 오늘에서야 Visual Studio를 재시작시에 CopySourceAsHtml 에서 오류가 발생한다는 것을 알게되었습니다.
확실히 점검하지 않고 글을 등록한점 정말 죄송합니다. ( _ _ )
저는 Vista환경에 Visual Studio 2008은 beta2 영문판이 설치되어 있으며, Visual Studio 2005는 한글판이 설치되어 있는데, 2005, 2008 모두에서 문제가 발생하더군요.
또한 프로젝트 속성 클릭시에서 오류가 발생하고 있었습니다.

우선 발생하는 문제들을 해결하는 방법은 유경상님의 포스트 하단 댓글로 작성된 "하늘로사랑한신발" 박길선님의 홈페이지 잘 소개되어 있습니다. (박길선님께 감사드립니다)

우선 원본 배포사이트에서 "CopySourceAsHtml 2.0.0 Source (91 KB)" 를 다운로드 받습니다.

박길선님 홈페이지로 들어가셔서 "Copy Source As Html" rev 0.2 게시물에 적혀 있는데로 코드를 수정합니다.

그런데, 박길선님 홈페이지 등록된 수정방법대로 수정후에 반영시키면 한글을 제대로 인식하지 못하는 문제가 발생합니다. 유경상님 소스를 역컴파일하여 수정하시어 소스는 역시 공개하지 않으셨네요. 하지만 문제만 해결한다고 해서 한글이 지원되지 않으면 아무런 필요가 없죠.. -_-;

한글 문제를 해결하기 위해서는 RtfParser.cs를 추가로 아래와 같이 수정하셔야 합니다.
우선은 아래와 같이 수정하시면 한글을 지원합니다. 유경상님 님께서 동일하게 수정하셨는지는 몰라도 역컴파일하지 않았으니 상관없겠죠? 66 - 69 라인을 추가해주시면 됩니다.

RtfParser.cs

   25 protected void ParseRtf(string rtf)

   26 {

   27 

   28     int first;

   29     int last;

   30     int index;

   31     int mark;

   32     string controlWord;

   33     bool hasParameter;

   34     long parameter;

   35 

   36     first = 1;

   37     last = rtf.Length - 1;

   38     for (index = first; index < last; index++)

   39     {

   40 

   41         switch (rtf[index])

   42         {

   43             case '{':

   44                 index += ParseOpeningBrace(rtf, index + 1);

   45                 break;

   46             case '}':

   47                 index += ParseClosingBrace(rtf, index + 1);

   48                 break;

   49             case '\\':

   50                 index++;

   51                 if (char.IsLetter(rtf[index]))

   52                 {

   53                     mark = index;

   54                     while ((index < last) && (char.IsLetter(rtf[index])))

   55                     {

   56                         index++;

   57                     }

   58                     controlWord = rtf.Substring(mark, index - mark);

   59                     if ((index < last) && ((char.IsDigit(rtf[index])) || (rtf[index] == '-')))

   60                     {

   61                         mark = index;

   62                         while ((index < last) && ((char.IsDigit(rtf[index])) || (rtf[index] == '-')))

   63                         {

   64                             index++;

   65 

  66                             if (controlWord.Equals("uc"))

   67                             {

   68                                 break;

   69                             }

   70                         }

   71                         hasParameter = true;

   72                         parameter = Int64.Parse(rtf.Substring(mark, index - mark));

   73                     }

   74                     else

   75                     {

   76                         hasParameter = false;

   77                         parameter = 0;

   78                     }

   79                     if (rtf[index] != ' ')

   80                     {

   81                         index--;

   82                     }

   83                     index += ParseControlWord(rtf, index + 1, controlWord, hasParameter, parameter);

   84                 }

   85                 else

   86                 {

   87                     index += ParseControlSymbol(rtf, index + 1, rtf[index]);

   88                 }

   89                 break;

   90             case '\r':

   91             case '\n':

   92                 break;

   93             default:

   94                 index += ParseLiteralText(rtf, index + 1, rtf[index]);

   95                 break;

   96         }

   97     }

   98 }

수정완료후 Build 하시고 CopySourceAsHtml.dll 파일과 CopySourceAsHtml.pdb 파일을 AddIns폴더로 덮어써주시면 문제가 해결이 되며, 한글도 제대로 출력됩니다.

혹시 문제가 발생하신다면 댓글로 남겨주세요..

Posted by WHiSTLE

트랙백 주소 http://blog.ntils.com/trackback/23 관련글 쓰기

댓글을 달아 주세요

Microsoft Patner 교육엘 다녀왔습니다. 처음 .NET을 접했을때 교육해주셨던 선생님이 Microsoft로 직장을 옮기셨는데, 이번 교육을 직접 진행하신다고 하길때 반가운 마음에 달려갔습니다.

교육은 Visual Studio 2008에 포함될 C# 3.0의 언어적인 발전부분에 대해서 Hands On Lab방식의 실습으로 이루어졌습니다. 사실 그동안 C# 3.0의 언어적인 발전부분에 대해서는 어느정도 관심은 있었지만, 게으른 관계로 깊이 공부해보지는 못했던 터라, 뜬 구름잡듯이만 알고 있었는데. 이번 교육을 통해서 개념을 잡을 수 있었던것 같습니다.

C# 3.0의 변화의 가장 핵심은 바로 Lambda Expression(람다식)의 지원이라고 할 수 있었습니다. 람다식을 지원하기 위해서 컴파일 타임의 형에 대한 추론능력이 비약적으로 발전하게 되었고, 그러한 타입에 대한 추론능력을 바탕으로 암시적 로컬 변수(Implicitly typed local variable) 및 익명타입(Anonymous Type)의 사용이 가능하게 되었으며, 개체와 컬렉션 이니셜라이저(Object and Collection Initializer)등으로 좀더 표현력이 뛰어난 언어로 발전할 수 있었던 것 같습니다.

그리고, 람다식이 가능하게 되므로서 C#언어 자체에서 데이터에 대한 SQL과 같은 구조적 질의또한 가능하게 되어 데이터의 활용이 한층 쉽고 강력해 진것도 느낄수 있더군요.

자동 구현 프로퍼티(Auto-Implemented Properties)

객체지향의 원칙중 캡슐화(Encapsulation)을 위하여 C#에서는 Property를 지원합니다. 하지만, 개발시에 private 접근자의 멤버변수와 이를 캡슐화하는 Property을 만들때 멤버변수로 접근하는 코드를 제외한 프로그래밍적인 처리가 들어가는 경우는 크게 많지 않습니다

private string name;

public string Name

{

    get { return name; }

    set { name = value; }

}

변수를 선언할때 위와 같이 거의 습관적으로 코딩하시는 분이 많을 겁니다. 저또한 거의 습관적으로 나오는 코드이고, 양이 많을 경우 매번 프로퍼티를 구현하는게 귀찮을 때도 많습니다. 물론 코드조각을 이용하시는 분들도 계시긴 하겠지만 저는 변수선언과 프로퍼티선언의 영역을 달리 하는 편이라 그것도 썩 맘에 들지 않아 그냥 손으로 코딩을 해버립니다.
물론 어쩔땐 get; set; 모두 노출해야 하는 멤버변수는 그냥 public으로 선언할 때도 있습니다.(-_-)

C# 3.0에서는 이러한 불필요한 듯한 노력을 줄여주는 자동 구현 프로퍼티가 반영되어 예전처럼 멤버변수와 프로퍼티의 구현을 기계적으로 할 필요가 없어졌습니다.
구문은 아래와 같습니다.

public string Name { get; set; }

public string Address { get; internal set; }

public string UserKey { get; private set; }

인터페이스에서 프로퍼티를 선언할 때와 거의 비슷한 구문을 가지고 있습니다만, 이는 인터페이스내에서 작성하는 코드가 아니라 클래스내에서 멤버변수의 선언과 프로퍼티의 선언을 한번에 해결할 수 있는 코드입니다.
하지만, 먼가 좀 이상하지 않으신가요? 프로퍼티란 메서드의 한 종류일뿐 분명히 변수는 아님에도 저게 어떻게 동작이 가능할까요? VB(.NET버전 이하)처럼 모든것을 포용해주는(가끔 프로그래머를 패닉상태로 몰고가기도 하죠.^^) C#이 아니지 않습니까? 깐깐한 C#이 변수도 선언하지 않았는데 메모리를 할당해 줄리는 만무 할텐데...
그것은 자동 구현 프로퍼티가 있으면 컴파일 타임에서 컴파일러가 멤버변수를 자동으로 생성해주게 되는 것입니다. 즉, 변수가 존재하지 않는게 아니라, 프로그래머가 해야할 일을 컴파일러가 대신해주는 것 뿐인거죠.

코드를 보시면 get; set;에 대하여 엑세스제한자를 지정할 수 있습니다. set에다 private를 지정할 수 있으므로 외부에서 값을 변경하는 것을 막을 수 있습니다.
하지만, 만일 set을 지정하지 않으면 내부에서 해당 변수에 접근이 불가능하기 때문에 get과 set 모두 존재해야지만 정상적인 사용이 가능합니다.

Posted by WHiSTLE
TAG C# 3.0

트랙백 주소 http://blog.ntils.com/trackback/24 관련글 쓰기

  1. 삭제

    검쉰의 생각

    2010/03/04 00:33 | Tracked from akyo's me2DAY

    C# 에 자동 구현 프로퍼티(Auto-Implemented Properties)라는게 생겼구나. 우왕 신기함. 기본서를 하나 사야되나.. 뭔가 많이 새롭네. ㄷㄷ

댓글을 달아 주세요

이전버튼 1 이전버튼