지난번 알아보았던 자동구현 프로퍼티(Auto-Implemented Properties)는 그동안 개발자들을 귀찮게 하던 멤버변수와 멤버변수에 대한 엑세스를 지원하는 프로퍼티를 생성하는데 좀 더 간결하게 표현할 수 있도록 해는 것이었다면 이번에 볼 객체와 컬렉션에 대한 이니셜라이저(Object and Collection Initializer)는 객체 또는 컬렉션의 생성을 좀더 직관적으로 할 수 있도록 해줍니다.
C# 2.0까지 객체를 생성할때 객체내의 멤버들을 동적으로 초기화 시키기 위해서는 생성자의 매개변수를 사용하거나, 생성후 객체의 각 멤버에 대해서 직접 값을 할당해주는 방법을 사용하였습니다만, C# 3.0의 Object and Collection Initializer는 생성자의 선언없이도 생성과 동시에 해당 객체나 컬렉션에 대한 초기화가 가능해지는 것입니다. 실제 코드를 보면 쉽게 이해가 가능하실 겁니다.
[코드 1-1 : C# 3.0 이전의 객체 초기화]
1publicclassCustomer
2 {
3privateint custKey;
4privatestring name;
5privatestring address;
6
7publicint CustKey { get { return custKey; } }
8publicstring Name
9 {
10get { return name; }
11set { name = value; }
12 }
13publicstring Address
14 {
15get { return address; }
16set { address = value; }
17 }
18 }
19
20publicclassProgram
21 {
22staticvoid Main(string[] args)
23 {
24Customer customer = newCustomer();
25 customer.Name = "오성민";
26 customer.Address = "서울시 구로구";
27 }
28 }
위의 코드는 언제나 보던 일반적인 코드입니다. 객체를 생성한 후 해당 객체의 멤버에 값을 할당하는 것이죠. 이 코드를 Object and Collection Initializer를 이용한 코드로 바꾼다면 아래와 같이 됩니다.
[코드 1-2 : C# 3.0 의 Object and Collection Initializer를 이용한 초기화
1publicclassCustomer
2 {
3publicint CustKey { get; privateset; }
4publicstring Name { get; set; }
5publicstring Address { get; set; }
6 }
7
8publicclassProgram
9 {
10staticvoid Main(string[] args)
11 {
12Customer customer = newCustomer
13 {
14 Name = "노휘겸",
15 Address = "서울시 관악구"
16 };
17 }
18 }
지난번에 보았던 자동구현 프로퍼티를 이용하여 Customer 클래스를 작성하였고, 이 클래스의 객체를 생성하는 시점에서 Customer객체의 내부 멤버들의 값을 할당하였습니다.
Object and Collection Initializer의 구성은 여러개의 멤버에 대한 이니셜라이저가 ,(콤마)로 구분되어 있으며, 이니셜라이저 외부에는 "{", "}"로 둘러싸여 구성되어 있습니다. 이니셜라이저는 접근이 가능한 멤버나 프로퍼티에 대해서만 값의 할당이 가능합니다. 즉, 위의 코드에서 본다면 set이 private 으로 구성된 CustKey의 경우에는 CustKey = 1 로 할당 할 수 없는 것입니다. 그리고, 한번에 같은 멤버를 두번이상 할당할 시에는 오류가 발생하게 됩니다.
아래는 위보다 조금 복잡한 사용예입니다.
[코드2]
1publicclassPoint
2 {
3publicint X { get; set; }
4publicint Y { get; set; }
5 }
6
7publicclassLine
8 {
9publicPoint P1 { get; set; }
10publicPoint P2 { get; set; }
11 }
12
13publicclassTriangle
14 {
15privatePoint p1 = newPoint();
16privatePoint p2 = newPoint();
17privatePoint p3 = newPoint();
18
19publicPoint P1 { get { return p1; } set { p1 = value; } }
20publicPoint P2 { get { return p2; } set { p2 = value; } }
21publicPoint P3 { get { return p3; } set { p3 = value; } }
22 }
23
24publicclassProgram
25 {
26staticvoid Main(string[] args)
27 {
28Line line = newLine
29 {
30 P1 = newPoint { X = 1, Y = 1 },
31 P2 = newPoint { X = 4, Y = 4 }
32 };
33
34Triangle triangle = newTriangle
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처럼 외부에서 할당이 불가능하고 생성자의 파라미터를 통해서 값을 할당해야 할때도 사용이 가능할까요? 물론 사용이 가능합니다. 생성자가 있다고 해서 달라질것은 생성자에 파라미터에 해당하는 값을 전송하면 되는 것이죠.
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을 만들때 멤버변수로 접근하는 코드를 제외한 프로그래밍적인 처리가 들어가는 경우는 크게 많지 않습니다
privatestring name;
publicstring Name
{
get { return name; }
set { name = value; }
}
변수를 선언할때 위와 같이 거의 습관적으로 코딩하시는 분이 많을 겁니다. 저또한 거의 습관적으로 나오는 코드이고, 양이 많을 경우 매번 프로퍼티를 구현하는게 귀찮을 때도 많습니다. 물론 코드조각을 이용하시는 분들도 계시긴 하겠지만 저는 변수선언과 프로퍼티선언의 영역을 달리 하는 편이라 그것도 썩 맘에 들지 않아 그냥 손으로 코딩을 해버립니다. 물론 어쩔땐 get; set; 모두 노출해야 하는 멤버변수는 그냥 public으로 선언할 때도 있습니다.(-_-)
C# 3.0에서는 이러한 불필요한 듯한 노력을 줄여주는 자동 구현 프로퍼티가 반영되어 예전처럼 멤버변수와 프로퍼티의 구현을 기계적으로 할 필요가 없어졌습니다. 구문은 아래와 같습니다.
publicstring Name { get; set; }
publicstring Address { get; internalset; }
publicstring UserKey { get; privateset; }
인터페이스에서 프로퍼티를 선언할 때와 거의 비슷한 구문을 가지고 있습니다만, 이는 인터페이스내에서 작성하는 코드가 아니라 클래스내에서 멤버변수의 선언과 프로퍼티의 선언을 한번에 해결할 수 있는 코드입니다. 하지만, 먼가 좀 이상하지 않으신가요? 프로퍼티란 메서드의 한 종류일뿐 분명히 변수는 아님에도 저게 어떻게 동작이 가능할까요? VB(.NET버전 이하)처럼 모든것을 포용해주는(가끔 프로그래머를 패닉상태로 몰고가기도 하죠.^^) C#이 아니지 않습니까? 깐깐한 C#이 변수도 선언하지 않았는데 메모리를 할당해 줄리는 만무 할텐데... 그것은 자동 구현 프로퍼티가 있으면 컴파일 타임에서 컴파일러가 멤버변수를 자동으로 생성해주게 되는 것입니다. 즉, 변수가 존재하지 않는게 아니라, 프로그래머가 해야할 일을 컴파일러가 대신해주는 것 뿐인거죠.
코드를 보시면 get; set;에 대하여 엑세스제한자를 지정할 수 있습니다. set에다 private를 지정할 수 있으므로 외부에서 값을 변경하는 것을 막을 수 있습니다. 하지만, 만일 set을 지정하지 않으면 내부에서 해당 변수에 접근이 불가능하기 때문에 get과 set 모두 존재해야지만 정상적인 사용이 가능합니다.