앞의 포스트에서 Object initializer에 대해서 알아봤다. 먼저 읽어보는 것이 좋을 듯 싶다. 이제 익명 타입(anonymous types)을 알아보자. C#3.0부터는 다음과 같은 표현이 가능해진다.

Customer c1 = new Customer{ Name="달봉이"};

var c2 = new Customer{Name="봉달이"};

var c3 = new {Name="봉봉이", Age=30 };

var c4 = new {c2.Name, c2.Age };

var c5 = new { c1.Name, c1.City};

var c6 = new {c1.City, c1.Name};

c1 생성은 앞에서 배운 객체 초기화 코드이다. c2는 Customer 객체를 초기화해서 var 타입에 할당하고 있다. var 키워드가 사용되면 할당된 표현식으로부터 변수의 타입을 유추해낸다. 해서 c2는 Customer 타입이라는 것을 알아낸다.

근데, c3,c4,c5,c6이 이상하다. 타입이름이 없다. 그렇다. 타입 이름이 없다. 굳이 타입 이름을 밝히지 않아도 되는 경우가 있다는 것이다. 그런 경우 굳이 이름을 지을 필요는 없다. 이런 경우는 컴파일러가 내부적으로 타입 이름을 지어주고 var의 타입 결정단계에서는 내부에서 생성한 타입으로 결정된다. 내부에서 자동 생성된 타입에는 initializer에서 사용된 공개 속성 또는 멤버에 해당하는 공개 속성과 해당 전용 멤버가 정의된다.  그 공개 속성과 내부 전용 멤버의 이름과 타입은  initializer에서 사용된 예를 들어 {Name="봉봉이", Age=30}를 통해서 유추된다.

c1~c6의 타입을 출력해보면 다음과 같다.

Console.WriteLine("c1 is {0}", c1.GetType());

Console.WriteLine("c2 is {0}", c2.GetType());

Console.WriteLine("c3 is {0}", c3.GetType());

Console.WriteLine("c4 is {0}", c4.GetType());

Console.WriteLine("c5 is {0}", c5.GetType());

Console.WriteLine("c6 is {0}", c6.GetType());

1155834665

LINQ 쿼리에서는 코드상에서 타입 이름은 필요없고, 단지 공개된 속성을 통해서 객체에 대한 쿼리만이 필요하다. 예를 들어 Name이 "달봉이"인 객체를 조회하는 쿼리문에서 타입이 뭔지는 필요없다. Name이라는 속성이 공개되어있고 그 타입이 string이면 된다.

앞의 포스트에서 C#1.x에서도 다음과 같은 표현이 가능했다고 했다.

string[] names = {"달봉이", "봉달이", "봉봉이"};

C#3.0에서는 배열을 초기화하는 표현이 var, 익명 타입과 object initializer를 이용해서 훨씬 강력해졌다.

var c = new [] {

    new { Name="달봉이", Sports=new[] { "테니스", "축구"} },

    new { Name="봉달이", Sports=new[] { "럭비", "골프"} },

    new { Name="봉봉이", Sports=new[] { "숨쉬기", "뒹굴기"} }

};

변수 c는 배열인데, 그 요소의 타입은 모두 이름이 없고 다만 속성으로 string 타입의 Name과 string배열의 Sports를 갖고 있는 요소들이다. 이 표현을 통해서 내부적으로는 타입의 이름보다는 그 타입의 구조만 알 수 있으면 된다는 것이다. 또한 주목할 것은 이 모든 표현이 한줄의 문장으로 이뤄졌다는 것이다. 즉 이런 표현이 쿼리 표현식 내에 표현될 수 있는 가능성이 확보된 것이다. 그런 의미에서 다시 한번 더 쿼리 표현식을 보자.

var query =

    from c in customers

    where c.Discount > 3

    orderby c.Discount

    select new { c.Name, Perc = c.Discount / 100 };

select 이하에서 익명 타입을 사용한 표현이 있다는 것을 알 수 있다. ^^

휴~~이제 거의 다 온 건가. 이제 마지막으로 람다 표현식에 대해서 알아보자.

Posted by dalbong2

현재 시리즈 제목 "LINQ시리즈"이다. 그러나 아직 본격적인 LINQ에는 들어가지도 못하고 있다. 지금 하나씩 설명하고 있는 단위 기술들 델리게이트, 익명 메소드, 제네릭, 타입 유추, 익명 타입 그리고 앞으로도 배울 람다 표현식을 포함한 몇 가지는 그 자체만으로도 가치가 있는 기술들이기는 하지만 뒤에서 설명할 LINQ에서 조합되어서 그 효과를 발휘하게 될 것이다. 그래서 표현식은 아조 아조 심플하게 변하게 된다. 한번 더 볼까나.

var query =

    from c in customers

    where c.Discount > 3

    orderby c.Discount

    select new { c.Name, Perc = c.Discount / 100 };

이렇게 간단한 표현속에 그렇게 많은 개념과 기술이 들어가 있을 줄이야. 필자도 미처 몰랐다.  이젠 좀 지겹기는 하다.  할것도 많은데. LINQ에 대한 본격적인 공부도 해야 하고. 그리고 필자의 원래의 목표였던 프레임워크 주제 특히 이번에는 Spring.NET에 대한 공부도 계속 하야 하는데. 그러나 이 표현식을 그냥 대충 넘어갈 수는 없을 것 같다. 완벽히 이해해야 할 것 같다. 그래야 앞으로의 개발자 생활이 편해질 것이라는 것이 필자의 동물적 생존 본능으로 느껴지고 있다. 빠샷!

오늘 Object initializer, 익명타입(anonymous type)이란 것을 함께 알아보자. 이 두 녀석도 철저히 코드를 심플하게 만들기 위한 개념들이다. 객체를 생성할때 그 내부 상태를 특정 상태로 초기화시켜 주고 싶다면 보통 파라미터가 있는 생성자(Constructor)를 사용합니다.  다음과 같은 타입이 있다고 하겠다.

public class Customer

{

    public string Name;

    public string City;

    public int Age ;

    public Customer(){}

    public Customer(string name, int age)

    {

        this.Name = name;

        this.Age = age;

    }

}

Customer의 객체는 name, age 파라미터를 갖는 생성자를 통해서 초기화될 수 있다. 만약 age가 아니라 city값을 초기화하고 싶다면 앞의 생성자 대신에 파라미터가 없는 기본 생성자와 공개적으로 노출된 속성을 통해 설정하는 다음과 같은 코드가 필요하다.

Customer c = new Customer();

c.Name = "Bart";

c.City = "Ghent";

C#3.0부터는 이렇게 객체를 초기화하는 코드를 간단히 할 수 있는 폼을 제공하고 있다.

Customer c = new Customer{ Name = "달봉이", City="Seoul" };

이런 표현을 Object initializer라고 한다. 인자가 없는 기본 생성자와 상태 설정이 필요한 값을 공개 속성 또는 필드로 노출시켜 두면 된다. 이 코드는 컴파일되어 IL코드로 되면 앞의 코드의 컴파일 결과와 동일해진다. {}사이에 명시된 이름들은 초기화되는 객체에서 공개적으로 노출한 속성 또는 필드에 해당한다.

앞에서처럼 타입과  시작 브래킷{ 사이에 ()이 없는 경우는 기본 생성자를 호출한다. 그러나 인자가 있는 생성자를 호출할 수도 있다.

// 파라미터가 있는 생성자를 초기화에 사용할 수도 있다.

Customer c = new Customer( "달봉이", 100 ) { City="Seoul" };

object initializer를 사용하면 함수 형태로 객체 초기화를 마무리할 수 있다. 즉 다른 문장을 사용하지 않고도 하나의 문장으로 복잡한 초기화를 끝낼 수 있다. 다음 코드를 보자.

Customer c = new Customer{

    Name = "Bart",

    Age = 23,

    Address = new Address {

                   Street = "Andersstreet",

                   Number = 60,

                   PostalCode = 9000,

                   City = "Ghent"

                }

  };

이렇게 객체안에 포함된 다른 객체의 초기화 코드도 하나의 문장으로 마무리될 수 있다. 이 표현도 다시 간단하게 변할 수 있는데, 내부의 중첩된 객체의 생성에서는 new를 없앨 수 있다. 최외곽의 생성자에서만 new를 사용해도 된다.

Customer c = new Customer{

    Name = "Bart",

    Age = 23,

    Address = {

                    Street = "Andersstreet",

                    Number = 60,

                    PostalCode = 9000,

                    City = "Ghent"

                }

  };

C#1.X에서도 이런 비슷한 표현이 있었다. 그러나 배열 생성에만 제한되어 있었다.

string[] names = {"달봉이", "봉달이", "봉봉이"};

이런 표현의 initializer는 컬렉션에서 자주 사용된다.

List<Customer> list = new List<Customer>{

    new Customer("달봉이", 100){ City = "Seoul"},

    new Customer{Name="봉달이"},

    new Customer{ Name = "봉봉이" City = "Seoul"},

};

요약하면, LINQ에서는 주로 그 쿼리의 대상들이고 그리고 그 쿼리 결과도 컬렉션으로 반환되는 경우가 많다. object initializer 표현은 LINQ에서 광범위하게 사용된다.  익명 타입(anonymous type)은 objct initializer로 인해서 심플해진 표현을 더 심플하게 해 준다.

포스트가 너무 길어진다. 익명 타입은 다음 포스트로 넘기자.

Posted by dalbong2