Welcome to Dynamic C#(9) - Dynamic Returns Again.

C# 2010. 1. 7. 09:00 Posted by 알 수 없는 사용자

- 정말 오랜만에 다시 Dynamic이군요.

안녕하세요~! 눈 때문에, 어떤 사람들은 로맨틱한 겨울이고, 어떤 사람들은 악마의 똥가루의 냄새에 신음하고, 어떤 사람들은 방에 콕처박혀 있고 뭐 아주 버라이어티한 겨울입니다. 겨울이 버라이어티 정신이 충만하네요. 연예대상같은거라도 하나 받고 싶은 가봐요. ㅋㅋ 아무튼! 정말 오랜만에 다시 dynamic시리즈를 쓰게 되네요. 워낙 한 내용도 없이 중간에 끊어서 좀 그랬습니다;;; 물론, 기다리신 분이 얼마나 있을지는 미지수지만요-_- 그럼. 한번 이야기를 시작해볼까요? dynamic에 대해서 조금씩 자세하게 들어가 보겠습니다.


- 어서내놔 dynamic

우선 예제를 하나 보시죠.

dynamic d = 10;
C c = new C();

//위쪽 그룹 
d.foo();
d.SomeProp = 10;
d[10] = 10;

//아래쪽 그룹 
c.Foo(d);
C.StaticMethod(d);
c.SomeProp = d;




위 그룹과 아래 그룹의 차이점은 뭘까요? 네~! Give that man a cigar!(누가 정답을 말했을때 하는 말이라네요) 위 그룹은 액션을 받는 객체가 동적인 객체, 즉 dynamic receiver이구요. 아래 그룹은 static receiver와 static method가 바로 차이점입니다.

위 그룹은 동적인 표현식(expression)속에서 직접적으로 동적인 행위가 일어나고, 아래그룹은 직접적으로  동적표현식은 아닙니다. 각각의 연산의 매개변수로 동적인 타입이 들어가면서, 전체적인 표현식을 간접적으로 동적으로 만들고 있는거죠. 이런 경우에는 컴파일러가 동적인 바인딩과 정적인 바인딩을 섞어서 수행하는데요. 예를 들어서 동적타입을 매개변수로 받는 오버로드가 있을 경우에, 어떤 멤버집합(member set)을 오버로드해야 할지 결정할때는 정적인 타입을 사용해서 판단할테구요, 실제로 오버로드를 판별(resolution)할때는 매개변수의 런타임 타입을 사용할 것이기 때문이죠.

컴파일러가 dynamic타입인 표현식을 보게되면, 그 안에 포함된 연산들을 동적 연산처럼 처리하게 됩니다. 즉, 표현식이 인덱스를 통한 접근이든 메서드호출이든 상관없이 그 표현식의 결과로 나오는 타입은 런타임에 결정될거라는 거죠. 그 결과로 컴파일 타임에 동적인 표현식의 결과로 나오는 타입은 dynamic이겠죠.

컴파일러는 이런 모든 동적인 연산들을 DLR을 통해서 dynamic call site라는 걸로 변환을 합니다. 지지지난 포스트에서 설명을 드렸던거 같은데요, 제네릭한 델리게이트를 가지고 있는 정적 필드입니다. 어떤 연산에 대한 호출을 가지고 있다가, 추후에 같은 타입의 연산이 호출되면 다시 call site를 생성할 필요없이 정적필드에 저장된 델리게이트를 호출해서 실행에 필요한 부하를 최대한 줄이는데 도움을 주는 친구죠. call site가 만들어지면, 컴파일러는 그 call site에 저장된 델리게이트를 호출할 코드를 생성하구요, 거기에다가 매개변수를 넘겨줍니다.

만약에, 호출한 객체가 IDynamicObject를 구현해서 스스로 동적 연산을 어떻게 처리할지 아는 객체가 아니거나, 미리 저장된 델리게이트와 타입이 안맞아서 캐시가 불발이 나면, call site와 같이 생성된 CallSiteBinder가 호출됩니다. CallSiteBinder는 call site에 필요한 바인딩을 어떻게 처리해야 하는지 알고 있는 객체인데요, C#은 이 CallSiteBinder에서 상속한 바인더를 갖고 있습니다. 이 C# CallSiteBinder가 적절한 바인딩을 통해서 DLR의 call site가 갖고 있는 델리게이트에 저장될 내용을 expression tree형태로 만들어서 리턴합니다. 이 내용역시 전전, 전포스트에서 다뤘었쬬? 못봤다고 하시면!!!! 제가 절대 가만있을수는 없는 문제고! 링크를 드..드리겠습니다. 전포스트, 전전포스트. 친절하죠?-_-


- 캐시되는 과정은 어떠냥

공개된 문서를 통해 볼 수 있는 현재의 캐시 방식은 그냥 단순히 매개변수들의 타입이 일치하는지 검사하는겁니다.  만약에.. 이런 호출이 있다고 할때...

args0.M(arg1, arg2, ...);

그리고, 이전에 args0이 C라는 타입이며, 매개변수 arg1과 arg2가 모두 int인 호출이 있었다고 해보면요, 캐시를 체크하는 코드는 대략아래와 같습니다.

if (args0.GetType() == typeof(C) &&
    arg1.GetType() == typeof(int) &&
    arg2.GetType() == typeof(int) &&
    ...
    )
{
    //CallSiteBinder의 바인드 결과는 여기에 계속 통합되구요
}
    ......//캐시 검사는 좀 더 많을 수도 있구요
else
{
    //여기서 CallSiteBinder의 bind메서드를 호출하고, 캐시를 업데이트 합니다.
}

지금까지 간단하게 알아본 내용을 그래도 마무리 하려면, C# CallSiteBinder가 뭘 어떻게 하는지를 알면 되겠네요. 서두에 두그룹의 연산중에 위 그룹의 연산을 보면요, 메서드 호출, 속성 접근, 인덱서 호출등 3가지 연산이 있었죠. 일단 모든 연산은요 표준 C# runtime binder를 통해서 생성되고, C# runtime binder가 걔네들을 데이터 객체로 사용합니다. 그 데이터객체는 바운드되야할 액션을 설명하는데요, 그런 객체를 C# payload라고 부른다고 합니다. 

C# runtime binder는 쉽게 작은 컴파일러라고 생각하면 되는데요, 얘가 일반적인 컴파일러가 갖고 있는 심볼테이블이나 타입시스템, 오버로드 판별 및 타입 교체같은 기능을 갖고 있기 때문입니다. 간단하게 d.Foo(1)을 예로 생각해보죠.

runtime binder가 호출되면, 현재 call site에 대한 payload과 call site에 대한 런타임 매개변수를 갖습니다. 그리고 dynamic receiver를 포함해서 그 모든 런타임 매개변수와 타입을 모아서는 그 타입에 대한 심볼테이블을 만듭니다.(심볼테이블에 대한 간략한 설명은 여기를 참조하세영!) 그리곤 payload꾸러미를 풀어헤쳐서 수행하려고 하는 연산의 이름을 꺼냅니다.(Foo) 그리고 d의 타입에서 리플렉션을 사용해서 Foo라는 이름을 갖는 모든 멤버를 뽑아냅니다. 그리고 걔네들도 심볼테이블에 적어넣죠. 말로 설명하니깐 깝깝하시죠? 설명하는 저도 깝깝하네여-_-;;; 제가 상상력을 동원해서 부연설명을 드리면요,

d.Foo(1)에서 먼저 매개변수의 타입과 d의 타입을 갖고와서 심볼테이블에 적어두고요.

주소     타입            이름
서울시   int             익명(= 1)

수원시   dynamic      d

그리고 리플렉션으로 d의 타입에서 Foo를 모두 찾아냈는데 대략 아래와 같다고 해보죠.
Foo(int a)
Foo(double b)
Foo(string c)

그리고 얘네들도 따로 심볼테이블에 집어넣으면?

-call site에 대한 심볼테이블
주소     타입            이름
서울시   int            익명(= 1)
수원시   dynamic      d

-d의 멤버중에 Foo라는 동명이인들
주소      타입        이름
부산시   void     Foo(int a)
창원시   void     Foo(double b)
안양시   void     Foo(string c)

그러면, 타입을 찬찬히 들여다보면, 어떤 Foo가 호출되야 할지 명확하게 보입니다. d.Foo(1)호출에서 매개변수의 런타임타입이 int이므로 Foo(int a)가 호출이 되겠죠. 이건 그냥 제가 설명을 위해서 상상력을 동원해본거니깐요 믿지는 마시기 바랍니다. 예비군 동원 무쟈게 귀찮으시져? 상상력도 무쟈게 귀찮아 하네요-_-. 어서 집에 보내고 다시 설명을 이어 가겠습니다.

위에서 설명드린 runtime binder를 설계할때 세웠던 한가지 원칙은 "runtime binder는 정적 컴파일러가 하는 짓을 똑같은 의미로 할 수 있어야 한다."였다고 하는데요. 그래서 에러메세지 역시 동일한 에러메세지를 뱉어낸다고 합니다.

위의 바인딩의 결과로 바인딩이 성공적일 경우에 수행할 동작을 표현한 expression tree가 만들어집니다. 그렇게 안되는 경우에는 runtime binder exception을 던진다고 하네요. 결과로 만들어진 expression tree는 DLR의 캐시에 포함되고 호출되면서 원래의 호출을 성공적으로 완료합니다.


- 약간의 제약사항?

그런데, 위에서 정적 컴파일러와 똑같은 짓을 하게 만들려고 했지만, 아마도 예산과 시간때문에 선택과 집중을 해야 하니깐 몇가지 못집어 넣은게 있다고 합니다. 람다식과 확장 메서드, 메서드 그룹(델리게이트)에 대한 이야기 인데요. 현재로서는 바인딩 안된 람다식을 런타임에서 표현할 방법이 없다고 합니다. 개발하다가 디버깅을 할때 브레이크 포인트를 잡고 그 상태에서 현재 상태의 객체에 값을 가져온다거나 메서드를 호출하고 값을 확인할 수 있잖아요? 근데, 람다식은 그런식으로 디버깅이 안됐던거 같은데, 아마 그문제가 계속 이어지는 거 같습니다.

그리고 메서드 그룹역시 런타임에 표현할 수 있는 방법이 없다고 합니다. 예를 들면,

delegate void D();
public class C
{
     static void Main(string[] agrs)
     {
        dynamic d = 10;
        D del = d.Foo; //뭐 이렇게는 안된다고 하네요. 그래서 런타임 익셉션이 난다고 합니다.
     }
}

그리고 확장 메서드 역시 using절과 범위를 바인딩없이 넘겨줄 방법이 없기때문에, 확장메서드역시 안된다고 하구요.


- 마물!

무척 오랜만의 포스팅인데요, 갈증이 조금이라도 해소가 되셨으면 좋겠네요. 제 실력이 바닥을 기다보니 원문의 내용을 한번 걸러서 드리는 정도밖에 못드리는 면이 많은데요. 뭐-_- 내공이 부족하니 한계가 명확하네요. 그럼 다음 포스트에서 뵙져~!!!!!!!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/11/02/dynamic-in-c-ii-basics.aspx


Welcome to dynamic C# 외전(3) - 감시하는 자와 감시당하는 자.

C# 2009. 12. 19. 09:00 Posted by 알 수 없는 사용자
오늘도 하루가 밝았어요.
상쾌한 기분으로 기지개를 켜고, 커튼을 젖혔어요.

오~ 마이~갓.
바깥에 왠 검은 외투를 입은 사람들이 망원경 뒤집어쓰고 이쪽을 째려보고 있어요.
아침부터 기분이 좋지 않아요.
자고일어나니 스타가 된걸까요, 왜 이쪽을 째려보는 건지 도무지 모르겠어요.
집밖을 나섰어요.
대놓고 따라와요.
집밖을 나왔는데도 왜 망원경은 계속 뒤집어 쓰고 있는건지 모르겠어요.
- [감시당하는 자]의 탐구생활


- 시작부터 탐구생활드립이냐.

그냥 한번 해보고 싶었을 뿐이니 노여워 마시길 바랍니다. 오늘은 감시하는 자와 감시당하는 자의 이야기를 해보려고 합니다. 즉, Observer패턴을 말하는 건데요. 뭔가 변화가 있을때 그 변화를 바로 알아차려서 어떤 동작을 수행해야 할때. 변화의 대상을 유심히 관찰하고 있어야 겠죠. 근데, 유심히 관찰하고 있는거 보다 변화가 일어났음을 알려주는 친구가 있다면 더 편리하겠죠. 이벤트 모델이 그중의 한 예 입니다. 이벤트가 일어났을때 통지를 받겠다고 등록을 해두면, 이벤트 발생시에 이벤트를 기다리는 모든 객체들에게 이벤트가 발생했음을 통지해줘서 적절한 조치를 취하도록 해주는 것 처럼 말이죠.

이런 건, 기존에는 직접 패턴을 통해 작성을 해야 했지만 닷넷 프레임워크 4.0에 이런 Observer패턴을 지원하기 위한 새로운 인터페이스가 추가 되었습니다. 거기에 대해서 설명을 드려볼까 합니다. 기존의 포스트와 마찬가지로 이번 포스트 역시 눈을 조심하시고 언제든지 OME를 외칠 준비를 하시길 바랍니다.



- 기존의 방식으로.

닷넷 프레임워크 4.0이전에는 어떻게 만들어야 했는지 한번 알아보겠습니다. 제 이해가 부족해서 적절치 못한 예제와 적절치 못한 수준일 수도 있으니, 마음의 준비 하시구요. 우선, UML 다이어그램을 한번 보시죠.

- 그림1. 예제의 UML 다이어그램.(인터페이스가 클래스모양을 하고 있는 것 같지만, 착시현상입니다. 이해를 위해서 부족한 실력이지만 그려봤습니다-_-)

네, 그림에서 느낌이 오듯이 라디오방송국과 청취자를 모델링해봤습니다. 제가 만드는거 중에 쓸모있는 건 별로 안나오죠-_-. 우선 RadioBroadcaster라는 인터페이스가 있구요, KBRRadioBroadcaster가 그걸 상속해서 방송국의 역할을 합니다. 그리고 각각의 Listener가 방송국에 주파수를 맞추고, 전달되는 전파를 수신하는 형태이구요.

enum BroadcastStatus
{
    StandBy = 0,
    OnAir = 1,
    End = 2
}

interface RadioBroadcaster//지켜볼 대상
{
    bool ListenTo(Listener listener);
    bool QuitFrom(Listener listener);
    void DoBroadcasting();
    void StartBroadcast();
}


네, 일단 방송의 상태를 알려주는 열거자와 지켜볼 대상이 되는 인터페이스입니다.


class KBRRadioBroadcaster : RadioBroadcaster//정보의 공급자
{
    private List<Listener> listeners = new List<Listener>();
    public BroadcastStatus broadcastStatus = BroadcastStatus.StandBy;

    private List<string> scripts = new List<string>
    {
        "안녕하십니까. KBR방송국의 김병만 기자입니다.",
        "요즘 살림살이 초큼 어찌 나아지셨슴까?",
        "안 나아지셨다구요? 그럼 이 방송에서 취업 필살전략을 알려드립니다.",
        "요즘같이 취업이 어려운때 3개 국어가 가능하면 취업 직빵이겠죠.",
        "따라 해보시져, Handle いぱい 꺽어.(핸들 이빠이 꺽어).",
        "3개국어 정말 쉽져? 이것만 한다면 당신도 취업계의 슈퍼스타!"
    };

    private int currentScriptIndex = 0;

    public bool ListenTo(Listener listener)
    {
        try
        {
            listeners.Add(listener);
        }
        catch (Exception ex)
        {
            return false;
        }

        return true;
    }

    public bool QuitFrom(Listener listener)
    {
        if (listeners.Contains(listener))
        {
            listeners.Remove(listener);
            return true;
        }

        return false;
    }

    public void DoBroadcasting()
    {
        if (scripts.Count.Equals(currentScriptIndex))
        {
            Console.WriteLine("방송끗.");
            broadcastStatus = BroadcastStatus.End;
        }
        else
        {
            foreach (Listener listener in listeners)
            {
                listener.Listening(scripts[currentScriptIndex]);
            }
            currentScriptIndex++;
        }
    }

    public void StartBroadcast()
    {
        broadcastStatus = BroadcastStatus.OnAir;
    }
}


그리고 방송국입니다. 듣고 싶은 청취자는 ListenTo메서드를 통해 주파수를 맞추고 QuitFrom을 통해 라디오를 끕니다. 그리고 DoBroadcasting을 통해 정해진 대사가 한번에 하나씩 전파를 통해 나가구요.


class Listener//지켜보는 행위의 주체
{
    private string name;

    public Listener(string name)
    {
        this.name = name;
        Console.WriteLine("나는야 {0}. 라디오를 가진 남자지.",this.name);
    }

    public void ListenToKBRRadio(RadioBroadcaster radioBroadcaster)
    {
        bool status = radioBroadcaster.ListenTo(this);

        if (status)
        {
            Console.WriteLine("{0} : 수신감도 조쿠나. ㅎㅎㅎㅎ", this.name);
        }
        else
        {
            Console.WriteLine("{0} : 아놕. 라디오도 못듣능구나", this.name);
        }
    }

    public void QuitFromKBRRadio(RadioBroadcaster radioBroadcaster)
    {
        bool status = radioBroadcaster.QuitFrom(this);

        if (status)
        {
            Console.WriteLine("{0} : 아놔. 완전 재미없엉", this.name);
        }
        else
        {
            Console.WriteLine("{0} : 아. 듣고 있던거 아니구낭", this.name);
        }
    }

    public void Listening(string script)
    {
        Console.WriteLine("{0} : {1}", this.name,script);
    }
}


그리고 이번엔 지켜보는 자, 청취자입니다. 라디오를 청취하고, 끊을 수 있으며, Listening을 통해서 매번 날아오는 전파를 수신합니다.

class Program
{
    static void Main(string[] args)
    {
        Listener listener1 = new Listener("마논개");
        Listener listener2 = new Listener("코턱용");
        Listener listener3 = new Listener("송순신");

        KBRRadioBroadcaster kbrRadioBroadcaster = new KBRRadioBroadcaster();
        listener1.ListenToKBRRadio(kbrRadioBroadcaster);//마논개 라디오 청취시작.
        int count = 0;
        do
        {
            if (count == 0)
            {
                kbrRadioBroadcaster.StartBroadcast();
            }
            if (count == 2)
            {
                listener2.ListenToKBRRadio(kbrRadioBroadcaster);//코턱용 라디오 청취시작
            }
            if (count == 3)
            {
                listener1.QuitFromKBRRadio(kbrRadioBroadcaster);//마논개 라디오 끔
                listener3.ListenToKBRRadio(kbrRadioBroadcaster);//송순신 라디오 청취시작
            }
            kbrRadioBroadcaster.DoBroadcasting();
            count++;
            Thread.Sleep(500);
        } while (kbrRadioBroadcaster.broadcastStatus != BroadcastStatus.End);
       
    }
}


그리고 프로그램 코드지요. 마논개, 코턱용, 송순신 세명이 라디오를 청취하는데요. 처음에는 마논개 혼자 듣다가, 두번이후에 코턱용이 라디오를 듣기 시작합니다. 그리고 세번째가 되면, 마논개가 라디오가 지겨워서 끄구요, 송순신이 라디오를 듣기 시작합니다. 방송이 끝날때까지, 0.5초당 한번씩 전파를 타고 방송이 날아오구요. 아웃사이더쯤되야 가능한 속도겠네요. 실행화면을 보시면 아래와 같습니다.

-그림2. 실행한 모습!

넵, 의도했던대로 방송이 전파를 타고 나오면서 사람들이 방송을 들었다가 빠집니다. 대사가 다 나온후에 방송이 종료되구요. 제 내공의 문제로 정확한 Observer패턴의 형태나, 사용방법이라고 볼 수는 없겠지만 뭐 이해에는 문제가 없을 것 같습니다. 하하하-_-;;;


- 이제 새로운 걸 내놔봐.

넵. 이젠 닷넷 4.0에 추가됐다는 새로운 인터페이스 IObservable<T>와 IObserver<T>에 대해서 말씀드려보도록 하겠습니다. IObserable<T>는 지켜볼 대상이 되는 클래스가 구현을 할 인터페이스입니다. 위에서 보자면 방송국이 되겠죠. 그리고 IObserver<T>는 지켜보는 행위를 하는 클래스가 구현을 할 인터페이스입니다. 마찬가지로 청취자가 되겠죠. 즉 IObservable<T>를 구현하는 클래스는 말그대로 observable한(지켜볼 수 있는) 클래스를 말하는 거구요, IObserver<T>를 구현하는 클래스도 말 그대로 observer클래스(지켜보는)가 되는 거죠. 그럼, 위의 예제를 여기에 맞춰서 옮겨 볼까요~~~~! 그전에, 일단 다이어그램을 한번 보시져.

-그림3. 새로운 인터페이스를 활용한 다이어그램


enum BroadcastStatus
{
    StandBy = 0,
    OnAir = 1,
    End = 2
}

interface RadioBroadcaster : IObservable<string>//지켜볼 대상
{       
    void DoBroadcasting();
    void StartBroadcast();
}


RadioBroadcaster가 IObservable<string>을 상속하고 있죠? 지켜볼 대상이 되기 때문이죠. 그리고 string은 지켜보는 observer가 전달받는 데이터를 말하는데요, 방송국에서 string타입의 전파를 뿌렸으므로 string이 됩니다.

class KBRRadioBroadcaster : RadioBroadcaster//정보의 공급자
{
    private List<IObserver<string>> listeners = new List<IObserver<string>>();
    public BroadcastStatus broadcastStatus = BroadcastStatus.StandBy;

    private List<string> scripts = new List<string>
    {
        "안녕하십니까. KBR방송국의 김병만 기자입니다.",
        "요즘 살림살이 초큼 어찌 나아지셨슴까?",
        "안 나아지셨다구요? 그럼 이 방송에서 취업 필살전략을 알려드립니다.",
        "요즘같이 취업이 어려운때 3개 국어가 가능하면 취업 직빵이겠죠.",
        "따라 해보시져, Handle いぱい 꺽어.(핸들 이빠이 꺽어).",
        "3개국어 정말 쉽져? 이것만 한다면 당신도 취업계의 슈퍼스타!"
    };

    private int currentScriptIndex = 0;

    public void DoBroadcasting()
    {
        if (scripts.Count.Equals(currentScriptIndex))
        {
            Console.WriteLine("방송끗.");
            broadcastStatus = BroadcastStatus.End;
        }
        else
        {
            foreach (IObserver<string> listener in listeners)
            {
                listener.OnNext(scripts[currentScriptIndex]);
               
                // Assume that we've arrived at location of Latitude has changed by 4.
                if (broadcastStatus == BroadcastStatus.End)
                {
                    listener.OnCompleted();
                }
            }
            currentScriptIndex++;
        }
    }

    public void StartBroadcast()
    {
        broadcastStatus = BroadcastStatus.OnAir;
    }

    #region IObservable<Listener> Members

    public IDisposable Subscribe(IObserver<string> listener)
    {
        listeners.Add(listener);
        // Announce current location to new observer.
        Console.WriteLine("{0} : 수신감도 조쿠나. ㅎㅎㅎㅎ", (listener as Listener).Name );

        return listener as IDisposable;
    }

    #endregion

    public void UnSubscribe(IObserver<string> listener)
    {
        listeners.Remove(listener);
        Console.WriteLine("{0} : 아놔. 완전 재미없엉", (listener as Listener).Name);
    }
}


변화가 있다면, IObservable의 메서드인 Subscribe를 구현했다는 것과 QuitFrom이 이름을 맞추기 위해서 UnSubscribe로 바뀐건데요. Subscribe는 ListenTo가 그랬듯이 변화가 생기면, 그걸 알려달라고 등록하는 통로가 됩니다.

class Listener : IObserver<string>//지켜보는 행위를 하는 주체
{
    private string name;

    public string Name
    {
        get
        {
            return name;
        }
    }

    public Listener(string name)
    {
        this.name = name;
        Console.WriteLine("나는야 {0}. 라디오를 가진 남자지.", this.name);
    }

    #region IObserver<string> Members

    public void OnCompleted()
    {
        Console.WriteLine("{0} : 음 재밌네 ㅋㅋㅋㅋㅋㅋㅋ", name);
    }

    public void OnError(Exception error)
    {
        Console.WriteLine("{0} : 아놔 감도 완전 좌절이네.", this.name);
    }

    public void OnNext(string script)
    {
        Console.WriteLine("{0} : {1}", this.name, script);
    }

    #endregion
}


그리고 요건 청취자죠. 지켜봐야 하기 때문에 IObserver<string>을 구현한게 보이시죠? 그리고 인터페이스의 메서드를 구현했는데요. 각각 완료시에 할 동작을 담을 OnCompleted와 에러발생시에 처리할 OnError, 그리고 기존의 Listening과 같이 변화를 전달받을때 할 동작을 담을 OnNext로 나눠집니다.


class Program
{
    static void Main(string[] args)
    {
        Listener listener1 = new Listener("마논개");
        Listener listener2 = new Listener("코턱용");
        Listener listener3 = new Listener("송순신");

        KBRRadioBroadcaster kbrRadioBroadcaster = new KBRRadioBroadcaster();
        kbrRadioBroadcaster.Subscribe(listener1);//마논개 라디오 청취시작.
        int count = 0;
        do
        {
            if (count == 0)//방송시작
            {
                kbrRadioBroadcaster.StartBroadcast();
            }
            if (count == 2)//잠시후 코턱용 라디오 청취시작
            {
                kbrRadioBroadcaster.Subscribe(listener2);
            }
            if (count == 3)//잠시후
            {
                kbrRadioBroadcaster.UnSubscribe(listener1);//마논개 라디오 끔
                kbrRadioBroadcaster.Subscribe(listener3); //송순신 라디오 청취시작
            }
            kbrRadioBroadcaster.DoBroadcasting();
            count++;
            Thread.Sleep(500);
        } while (kbrRadioBroadcaster.broadcastStatus != BroadcastStatus.End);
    }
}


그리고 프로그램 코드죠. 동작은 위와 동일합니다. 결과를 보시죠.

-그림4. 동일한 결과


- 마무리.

의의를 찾자면, 프레임워크 레벨에서 Observer패턴을 지원하면서 좀 더 정돈된 형태로 구현을 할 수 있다는 것과 그렇게 구현을 했을때 나중에 프레임워크 레벨에서 성능 개선이 있던지 하면, 그런 혜택을 코드의 변경없이 누릴 수 있다는 점이 있겠구요. 또 한가지 점은 프레임워크나 다른 기술에서 내부적으로도 이 인터페이스를 활용하게 될거고, 그리고 새로운 형태의 프레임워크도 등장을 기대할 수 있다는 점일 것 같습니다.

실제로, 이 인터페이스를 통해서
Reactive Extensions for .NET(줄여서 Rx) 라는 프로젝트가 Devlab에 공개되었습니다. 스크린캐스트를 살짝 들여다보니, 흥미로운 응용이 가능하더군요. 가능하면 다음번에는 Rx를 가지고 이야기를 한번 해보고 싶네요. 그럼, 시력을 떨어뜨리는 글을 열심히 읽어주셔서 감사합니다. 날도 추운데 피드백은 따쓰하게... 아시져? ㅋㅋㅋㅋ 그리고! 소스는 첨부해드립니다.


- 참고자료

1. http://msdn.microsoft.com/en-us/library/dd990377(VS.100).aspx





저희 Viva 2010 팀은 차세대 개발 플랫폼인 Visual Studio 2010, Visual Studio Team System 2010, C# 4.0, C++ 0x, Cloud 등을 공부하고 다양한 매체를 통해 알리는 팀 입니다.

저희 VSTS 2010 팀은 최근 Visual Studio 2010 베타 2 공개와 앞으로 곧 나오게 될 RC 및 정식 버전 출시에 발 맞춰서 활동을 더욱 강화하기 위해서 팀 멤버를 추가로 모집하고 있습니다.


그간의 활동

온라인 활동

http://vsts2010.net 블로그를 통해서 총 164개의 Article 발행, 총 방문자 약 50000, 일 평균 300명의 방문


오프라인 활동


[2009년 6월 10일 MSDN 주간 세미나]

강보람 - C#연대기 -C#의 Before/After

공성의 - VSTS2010에서의 소프트웨어 품질 관리

김병진 - VSTS 2010 Architecture & UML

엄준일 - Managed Extensibility Framework

최흥배 - Visual C++ 10, C++0x 그리고 Concurrency Runtime


[TechDays 2009 참여]

김병진, 강성재 - Visual Studio Team System 2010 Overview

최흥배새로운 시대를 여는 Visual C++ 10

강보람C# 4.0 with dynamic : 사랑과 전쟁. 그들의 4주 후…

조진현Multi-threaded rendering in DirectX11

엄준일, 공성의Visual Studio Team System 2010 With Agile

김대욱, 김태균WPF4.0 을 위한 VIsualStudio 2010



활동 영역

온라인 활동 영역

팀 블로그 활동

팀 블로그를 통해 자신의 글을 게시할 수 있습니다. 현재 수백 명의 정기 구독자에게 글이 공개가 되며, 팀 블로그가 구글 등의 검색 상위권에 이르게 됨으로 자신의 글이 상위 검색에 노출되는 간접적인 혜택을 누릴 수 있습니다.

온라인 세미나

한국 마이크로소프트와 팀 자체에서 진행하는 여러 가지 온라인 세미나의 스피커로 활동하게 됩니다.

온라인 커뮤니티(예정)

온라인 커뮤니티 활동과 함께 커뮤니티 운영 활동을 하게 됩니다.

   

오프라인 활동 영역

오프라인 스터디

오프라인 스터디를 통해 자신의 분야를 공부하고 발표합니다. 그리고 좋은 콘텐츠는 곧바로 온라인/오프라인 세미나 스피커 활동으로 이어집니다.

오프라인 세미나

한국 마이크로소프트와 팀 자체에서 진행하는 오프라인 세미나의 스피커로 활동할 수 있습니다. 팀 자체에서는 매월 오프라인 정기 세미나를 진행하며, 자신의 노하우를 오프라인 세미나를 통해 전달할 수 있는 기회를 드립니다.

기고

팀 블로그를 통해 축적된 자신의 콘텐츠는 월간 잡지 등에 기고할 수 있습니다.

책 집필, 번역(예정)

다양한 노하우를 책으로 집필하고, 외국의 유명 서적을 번역하는 활동을 계획하고 있습니다.

Microsoft MVP 추천

MVP 에 되고자 하시는 분은 한국 이크로소프트 직원과 마이크로소프트 MVP 의 추천을 드립니다.

   

모집 분야

  • Cloud Development
  • C#
  • VB.NET
  • C++
  • Agile Development
  • Parallel Development
  • Web Development
  • ASP.NET
  • Silverlight
  • Windows 7 Development
  • RIA Development
  • Architect Development
  • Office Business Application Development
  • .NET Framework 4.0
  • Visual Studio 2010
  • Visual Studio Team System 2010
  • ETC…

   

마감

정해진 마감 일자는 없습니다만, 적어도 12월 중에는 저희와 함께 할 수 있었으면 합니다. 가능한 빨리 지원해주시길 부탁드립니다.

   

지원 방법

아래의 양식을 채워주시고 kkongchi@gmail.com 으로 보내주세요.

이름

  

사진

  

블로그

  

전화번호, 이메일

  

티스토리 아이디

  

소개

(직업 및 회사명 포함)

관심 분야

(중복 가능)

   

지원해 주신 내용에 대한 심사 후, 오프라인 인터뷰를 통해서 멤버를 선정하게 됩니다. 이번에는 특히 활발한 온라인 활동, 특히 개인 블로그 활동을 많이 해오신 분들을 우선적으로 선정할 예정입니다.

   

지원 시 유의 사항

참고로 저희 스터디에서는 배우고자 지원하시는 분들은 선발하지 않습니다. 저희 팀의 스터디에서는 여러분들에게 아무것도 가르쳐주지 않습니다.

저희 팀에서는 절대 실력을 보고 맴버를 선발하지 않습니다. 물론 실력이 출중하면 좋겠지만 새로운 VSTS 2010 분야는 어느 누구도 밟아보지 않은 새로운 황야와 같습니다. 새로운 길을 함께 가실 활동력이 충분하신 분들은 꼭 지원해 주시기 바랍니다. ^^

Welcome to dynamic C# 외전(2) - Generic Method.

C# 2009. 11. 30. 08:30 Posted by 알 수 없는 사용자

- 또 외전이군 ㅋ

오랜만의 포스팅이네요. 넵. 또 외전입니다-_-;;; 최근에 다른 일때문에 포스팅을 못하다가 어떤 계기가 생겨서 포스팅을 하게됐습니다. 음;; 제네릭 메서드를 사용해서 코딩할 일이 있었는데요, 갑자기 전에 어떻게 했는지 기억이 안나는거 있죠-_-. 그래서, 기억을 더듬고 검색해서 코딩을 했습니다. 그래서 저도 안까먹고, 혹시 이거때매 해매는 분들을 위해서 포스팅을 할까 합니다.

* 경고 : 늘 그렇지만 제가 하는 포스팅은 허접합니다. 왜냐면, 제가 허접하기 때문이죠. 포스팅의 허접함에 눈이 아프시다면, "Oh My Eyes!"를 한번 외치시고 뒤로가기 버튼을 활용하시면 직빵입니다. ㅋ OME는 스타에서만 나오는게 아니거등요.


- 제네릭?

넵. 제네릭은 불필요한 박싱/언박싱을 없애도록 도와준 고마운 기능입니다. C# 2.0에서 추가가 됐었죠. 내부적으로는 F#에서 유용하다고 검증이 돼서, C#에 추가하기로 결정을 했었다고 합니다. F#이 쫌 까칠하긴 해도, 파워풀 하거든요^^(까칠하다보니, F#이랑 쫌 잘지내보려고 데이트도 11번 정도 했지만 일단은 손 놨습니다. 상처받은 마음을 C#과 좀 달래려구요. 근데 요즘은 C#한테도 상처를....)

혹시 모르는 분들을 위해서 허접한 실력으로 부연설명을 드리자면요. 박싱(boxing)은 포장을 하는 거구요, 언박싱(unboxing)은 포장을 푸는겁니다. 즉, C#에는 크게 타입이 크게 두가지로 분류가 되는데요. 값형식(value type)과 참조형식(reference type)입니다. 값형식은 변수의 이름이 가리키는 곳에는 실제 값이 들어있고, 참조형식은 변수의 이름이 가리키는 곳에 실제값에 대한 참조가 있습니다. 즉, C에서의 포인터의 개념을 생각하시면 됩니다. 포인터는 실제값을 가지는게 아니라, 값이 있는 곳의 주소를 가지고 있는거니까요.

근데, 값형 타입이 닷넷의 최상위 타입인 object같은 참조형 타입과 같이 처리해야 되는 경우가 있습니다. 예를 들면, C# 1.0에서는 컬렉션이 object형만 받을 수 있었습니다. 그래서, int타입만 처리하는 경우에도 컬렉션을 사용하려면, object타입으로 int타입을 포장해서 집어넣고, 빼낼때도 object타입의 포장을 풀고 int타입을 꺼내와야 했던거죠. 그런데, 이게 너무 삽질이다 보니 더 편하게 할 수 있는 방법으로 제네릭이 등장 했습니다.

제네릭은 어떤 타입도 될 수 있는 걸 말하는데요, List<int>는 int타입만 받는 컬렉션, List<string>은 string타입만 받는 컬렉션이 되면서, 불필요한 박싱/언박싱도 없어지고 코드도 훨씬 명확해 지는 장점이 있습니다. 그런데, 꼭 이럴때 뿐만 아니라도 제네릭이 필요한 경우가 생기는데요. 오늘 제가 설명드리면서 보여드릴 예제가 그런 경우입니다.


- 그래그래. 예제 내놔봐.

예제는 XML로 객체를 시리얼라이즈(Serialize)하고 다시 디시리얼라이즈(Deserialize)하는 건데요. 시리얼라이즈을 할때는 시리얼라이징 하는 객체의 타입을 명시해줘야 합니다. 그래서 한 프로그램에서 여러객체를 시리얼라이즈해야 할때는, 그 수만큼 오버로딩이 되기도 합니다. 하지만, 제네릭이 출동한다면?

제.

네.

릭.

...죄송합니다. 스덕이다 보니, 스타와 관련된 드립을 하게 되는군요. 계속해서 말씀드리죠-_-. 어떤 타입이든지 될 수 있는 제네릭을 활용한다면, 메서드를 변경시키지 않고서도 여러타입에 사용가능한 메서드를 만들 수 있습니다. 그럼, 소스를 한번 보시죠.

 class Serializer
{
    internal static bool Serialize<T>(T source, string fileName)
    {
        try
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            Stream stream = new FileStream(fileName, FileMode.Create,
                   FileAccess.Write, FileShare.None);
            serializer.Serialize(stream, source);
            stream.Close();

            return true;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    internal static T Deserialize<T>(string fileName) where T : class, new()
    {
        T target;
        FileInfo _settingFile = new FileInfo(fileName);
        if (!_settingFile.Exists)
        {
            return new T();
        }

        try
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            Stream stream = new FileStream(fileName, FileMode.Open,
                   FileAccess.Read, FileShare.None);
            target = serializer.Deserialize(stream) as T;
            stream.Close();

            return target;
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }
}


위의 소스에서 <T>라고 쓰인부분이 바로 제네릭을 쓰겠다고 하는 겁니다. 즉, 어떤 타입이든지 될 수 있는 T라는 제네릭한 타입에 대해서 메서드를 작성하겠다는 거죠. 그리고 주목하실 부분이, Deserialize메서드 선언부의 끝부분의 "where T : class, new()"입니다. 이건, 제네릭한 타입 파라미터에 제약조건을 주는 겁니다. 즉, 위에 선언된 건 T가 제네릭한 타입이긴한데 class여야만 하고, 파라미터없고 public인 생성자가 있어야 한다는 겁니다. 그런 타입만 T를 통해서 넘어올 수 있다는 거죠. 제약조건은 그 외에도 아래와 같은 것들이 있습니다.

 제약조건  설명
 where T: struct  타입 매개변수는 반드시 Nullable을 제외한 값형(value type)이어야만 한다.
 where T : class  타입 매개변수는 반드시 참조형(reference type)이어야만 한다.
 where T : new()  타입 매개변수는 반드시 public이고 매개변수가 없는 생성자를 갖고 있어야 한다. 그리고 다른 제약 조건이랑 같이 사용될때는 new()가 맨뒤에 와야한다.
 where T : <base class name>  타입 매개변수는 반드시 명시된 클래스를 상속 해야한다.
 where T : <interface name>  타입 매개변수는 반드시 명시된 인터페이스이거나, 명시된 인터페이스를 구현해야 한다.
 where T : U  T에 해당되는 매개변수는 반드시 U에 해당되는 매개변수 타입이거나, U를 상속한 타입이어야 한다. 

네. 뭐 그렇답니다. 프레임워크 개발자나 복잡한 경우가 아니면, 저걸 다 쓸일이 있을지는 모르겠지만 알아두면 좋겠죠. 그리고 위의 코드는 아래와 같이 사용할 수 있습니다. 메서드가 static으로 된건 그냥 편의를 위해서 입니당.

public class Pair
{
    public int Id { get; set; }
    public string Name { get; set; }
}

class Program
{
    static bool SerializePairs(List<Pair> pairs)
    {
        try
        {
            Serializer.Serialize<List<Pair>>(pairs, "Pairs.xml");

            return true;
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
            return false;
        }
    }

    static List<Pair> DeserializePairs()
    {
        try
        {
            return Serializer.Deserialize<List<Pair>>("Pairs.xml");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return new List<Pair>();
        }
    }

    static void Main(string[] args)
    {
        List<Pair> pairs = new List<Pair>
        {
            new Pair{
                Id=1,
                Name="홍길동"
            },
            new Pair{
                Id=2,
                Name="루저"
            },
            new Pair{
                Id=3,
                Name="위너"
            }
        };

        SerializePairs(pairs);
        List<Pair> pairs2 = DeserializePairs();

        foreach (Pair pair in pairs2)
        {
            Console.WriteLine(
                string.Format("{0} : {1}",
                    pair.Id,
                    pair.Name)
                );
        }
    }
}


Pair라는 객체가 있고, 그 객체에는 홍길동과 루저, 위너 이렇게 3명이 들어갑니다. 그리고 그 객체를 시리얼라이즈하고, 다시 디시리얼라이즈해서 결과를 화면에 출력하고 있죠. 메서드를 호출할때 "Serialize<List<Pair>>(pairs, "Pairs.xml")" 이렇게, <>안에 제네릭한 타입인 T가 실제로 어떤 타입이 될지를 명시해주고 있습니다. 그리고 결과를 보면 아래와 같구요.


넵. 그렇습니다. 별거 없죠-_-;;;


- 여기까지.

넵. 여기까지입니다. 뭐 특별한 내용도 없지만, 그냥 한번 정리하면 저도 편하고 필요하신 분들에게도 편하지 않을까 싶어서 말이죠-_- ㅋ. 소스는 파일로 첨부해드리겠습니다. 필요하신분들은 참고하시구요~. 언제가 될지는 모르겠지만, 다음 포스트에서 뵙져!!!


- 참고자료
1. http://msdn.microsoft.com/en-us/library/d5x73970.aspx

Welcome to dynamic C# 외전(1) - Generate From Usage.

C# 2009. 10. 25. 09:00 Posted by 알 수 없는 사용자

- 베타2를 맞이하여.

비주얼스튜디오 2010 베타2가 드디어 나왔습니다. 모양도 초큼더 이뻐진거 같구요, 이제 슬슬 제품이 출시될거 같다는 느낌도 드는 군요. 제가 처음 비주얼 스튜디오를 접한게 비주얼스튜디오2002 베타2 버전이었는데요, 왠지 향수가 느껴지네요.-_-;;; 이번 포스트에서는 TDD의 대세를 따라서 코딩을 좀더 편리하게 해주는 기능하나를 소개해드리려 합니다.


- 이름하여 Generate From Usage!

모 이름은 '사용하는 모냥을 봐서 생성하겠다'정도가 되겠네요. 2008까지는 TDD방식으로 개발을 한다고 해도, 클래스등을 미리 만들어 놓고, 메서드 수준에 가서야 메서드 스텁은 생성하면서 TDD방식을 적용할 수 있었습니다. 물론, 편리한 툴의 지원을 받는 면에서 말이죠. 그런데 2010 베타2 부터는 TDD를 편리하게 할 수 있도록 좀 더 멋진기능이 지원됩니다. 한번 살펴보시져!


예들 들어서, Test123이라는 클래스를 TDD로 만들려고 합니다. 그러면, Test123은 당연히 현재 존재하지 않는 클래스이겠죠? TDD가 일반적으로 시나리오를 기반으로 테스트케이스를 먼저 만들고, 그 테스트를 통과하도록 구체적으로 코드를 작성해나가는 방식이니까요. 약간 귀찮은 문제가 여기서 발생합니다. 실제로 TDD를 해보면, 테스트 코드를 작성할때 대상이 되는 클래스를 열심히 타이핑하는데, 기존에 없는 클래스기 때문에 당연히 인텔리센스의 지원이 안되서 날코딩을 해야 하는거죠. 그래서 귀찮으니깐 없는 클래스를 껍데기만 미리 작성해놓고, 인텔리센스의 지원을 받습니다. 그리고 VS2008에서는 스텁생성이 메서드레벨에서만 지원됐기 때문에, 클래스를 선언해놔야 좀더 편리하기 때문이죠.

근데, 만약에 존재하지 않는 클래스라도 인텔리센스의 지원을 받을 수 있다면? 그리고 그 클래스의 스텁을 생성해줌과 동시에, 클래스 파일까지도 생성을 해준다면? 그리고 드라군이 출동한다면?? ........ 아무튼 *라게 많이 편하겠죠?

<그림1>을 보시면, 이런 멋진 기능이 이제 현실로 다가왔음을 느끼실 수 있습니다. 분명히 Test123은 없는 클래스니깐, 빨간 밑줄이 그어졌는데, new뒤에 나오는 인텔리센스에는 Test123이 버젓이 들어가 있습니다!!!



<그림1>버젓이 들어가 있다!!!!


인텔리센스에 버젓이 들어가 있었지만, 아직 정의안된 클래스니깐 <그림2>처럼 에러메세지가 나오는걸 보실 수 있습니다.


<그림2>클래스정의가 엄서요!!


하지만, 예전버전에서 없는메서드에 마우스 오른쪽 버튼을 눌러서 메서드 스텁을 생성했듯이, Test123위에 마우스를 놓고 오른쪽 버튼을 눌러보면, <그림3>처럼 클래스를 생성하는 옵션이 있습니다.



<그림3>참으로 착한 옵션이로닭!!!


그리고 결과를 보면, <그림4>처럼 Test123.cs라는 파일이 생성되었고, <그림5>처럼 껍데기가 작성된 걸 보실 수 있습니다!



<그림4>조...좋은 생성이다1



<그림5>조...좋은 생성이다2

그리고 이번엔, 없는 프로퍼티를 하나 써볼까요? <그림6>처럼 아직 정의가 안된 프로퍼티를 갖다쓰면, 어쩌구 저쩌구 하고 컴파일러가 불평을 합니다.



<그림6>어쩌구 저쩌구...


그러면, 클래스와 같이 오른쪽 버튼을 지그시 눌러주면, <그림7>처럼 필드나 프로퍼티로 생성을 할 수 있습니다. 프로퍼티를 눌러보면 <그림8>과 같이 프로퍼티가 하나 추가된걸 보실 수 있죠.



<그림7>프로퍼티 하나 추가여.



<그림8>조...좋은 생성이다3


- 마치면서

VS2008에서 TDD를 아주 허접하게 해본 경험으로 미뤄볼때, 확실히 좀더 잔손질을 줄여주는 기능이 될 거 같습니다. 뭐 꼭 TDD가 아니더라도 기능을 활용할 일은 많은 거라는 생각이 드네요. 전 이래서 비주얼 스튜디오가 좋습니다. ㅋㅋㅋ....-_-;;;

Welcome to Dynamic C#(8) - DLR이 나무를 사랑하는 이유

C# 2009. 9. 21. 09:00 Posted by 알 수 없는 사용자

- 아아... 이거슨 Love Story.

오늘 말씀드릴 내용은 DLR이 왜 Expression Tree(이하 표현나무)를 처음에는 박대하다가 나중에 가서는 "역시 난 니가 아니면 안되나봐 ㅠ_ㅠ"같은 대사를 날리게 되는지 Jim Hugunin의 증언을 통해서 알려드리고자 합니다. 그럼 지금부터 달콤 쌉싸름하고 무슨말인지도 잘 모르겠는 러브스토리를 한번 감상해보시져.


- 누군가가 필요하긴 했어. 하지만 너 아닌 다른 나무라도 괜찮을 거 같았어.

 지난 시간에 말씀드렸듯이 C# 3.0에 표현나무가 추가된 이유는 쿼리가 실행되는 곳이 어디냐에 따라서 최적화를 하기 위해서 컴파일된 IL코드의 형태보다는 자료구조의 형태가 훨씬 수월하기 때문이었습니다. 그리고 DLR은 닷넷 프레임워크에서 실행되는 동적 언어들이 공동으로 사용할 수 있는 기반을 제공해주고, 각 언어들끼리 그리고 정적언어들과도 서로 대화를 주고 받을 수 있도록 해주기 위한 장치인데요. 그렇다면, 언어별로 내부적인 구현의 차이점이 있을 수 있겠고 한 언어에서 작성한 코드를 다른 언어에서도 사용가능하려면, 둘 사이에 변환과정이 있어야 하겠고 그 변환과정을 수월하게 해줄 수 있는 도구가 필요하겠죠. 어떤 가요? DLR과 표현나무의 러브스토리의 윤곽이 좀 잡히시나요?

Jim Hugunin에 따르면 애초에 DLR을 설계할때 코드를 표현할 공통자료구조로 트리형태를 생각하고 있었다고 합니다. 그리고 타입이 없으면서 런타임에 late-bound되는 노드들로 구성된 트리로 구현을 시작했다고 합니다.  그래서 그 당시에 IronPython에 맞게 구현된 트리에는 타입이 있는 노드가 ConstantExpression딱 하나였다고 합니다. 상수 값이면 뭐든지 가지고 있는 노드 말이죠. 그리고 자신들이 생각하고 있는 트리와 표현나무 사이에는 공통점이 없다고 생각했었다고 합니다. 왜냐면 정적언어에서 쓰는 게 동적언어에는 안 맞을거라고 생각했기 때문이라는 군요. 그래서 독립적으로 트리를 구성해 나갔다고 하네요.


- 하지만, 다른 나무들을 만날 수록 니가 그립더라.

하지만, 다른 언어의 구현을 추가하면서, 기존의 DLR트리에 각 언어의 특징을 제대로 표현하기 힘들어졌다고 하는군요. 각 언어의 특징을 지원하기 위한 방법이 필요한데, 그렇다고 해서 직접적으로 그 언어의 특징을 위한 노드를 추가할 수 는 없었다네요. 아마도 DLR트리는 DLR에서 도는 언어들이 공통적으로 공유하게되는 자료구조인데, 특정언어에만 있는 노드를 추가하면, 점점 지저분해 지기 때문이겠죠.

예를 들면, Python의 print문을 보면, 내부적으로 print문은 Python에서 출력에 대한 걸 어떻게 처리할지에 대해 알고 있는 static 메서드를 호출한다고 하는군요. 그래서 DLR트리에 static 메서드 호출노드를 추가했다고 합니다. 그러면 print호출은 static 메서드를 호출하는 걸로 연결할 수 있으니까요.

그리고 이런 작업을 하다보니깐, 그동안 개발팀이 추가해오던 노드들이 정적인 타입이 적용되는 표현나무의 각 노드랑 딱 들어맞는 다는 걸 깨달았다고 합니다. 그래서 처음부터 다시 만드는 대신에 기존에 잘 쓰고 있는 걸 확장하자고 마음을 먹었고, 기존의 표현나무에 동적인 연산이나 변수, 이름 바인딩, 흐름제어등을 위한 노드를 추가했다고 합니다.

그리고 DLR에서 동작하는 언어가 이런 트리형태를 만들어 내게 되면서 각각의 언어에 맞는 최적화를 수행하는게 수월해졌습니다. 지난 포스트에서 설명드렸던 표현나무를 사용하면서 얻는 장점과 매우 동일한 점입니다. 그래서 각 언어의 컴파일러는 코드를 표현나무의 형태로 만들어서 그 표현나무를 DLR에게 전해준다고 하는 군요.


- 그들은 현재 잘 살고 있답니다.

Jim Hugunin의 PDC2008의 "Dynamic Languages In Microsoft .NET"의 슬라이드를 통해서 각 언어별로 나타나는 표현나무의 모양을 보도록 하겠습니다.

C#에서 구현한 팩토리얼을 표현나무로 옮긴다면 아래와 같다고 하는군요.



그리고 이걸 dynamic을 써서 C#에서 작성하면 아래와 같다고 합니다. 붉은 색으로 변화가 있는 부분이 있죠? 이 부분이 동적인 타입을 적용한 부분인데요, 즉 "==", "*", "-"같은 DLR의 표준메세지 객체를 이용해서 모든 언어에서 사용가능하도록 하면서 C#에서 왔다는 걸 표시해서 C# 바인더를 사용할 수 있게 한다고 합니다.



1과 2를 비교해보시면 어떤 노드가 추가됐는지 확일 할 수 있군요. 그리고 동일한 코드로 IronPython에서 만든 표현나무는 아래와 같습니다.



C#대신에 Python바인더를 사용하고 있구요, 메서드 호출부분에서 global field를 참조하는 부분이 있는데요, 이 부분은 닷넷의 프로퍼티를 통한 static field와 거의 완벽하게 들어 맞는다고 하는군요. 그래서 약간의 코드만 추가하면 된다고 합니다. 그리고 아래는 루비의 표현나무입니다.



루비는 함수형언어 개념의 표현식을 기반으로 하는데요 구문이라는게 없고 모든 루비의 코드 한라인 한라인은 값을 리턴하는 구조라고 하는군요. 그래서 return 같은 구문을 안써도 되므로 표현나무와 자연스럽게 어울린다는 군요.

즉, 각 언어에서 나온 트리가 모양이 조금씩 다르긴 한데 전체적으로 비슷한 구조를 유지하고 있습니다. 이런 각 언어에서 나온 표현나무를 분석해서 모든 트리에서 공유하는 폼으로 만들 수 있다는 군요. 아직 어떻게 정확하게 그렇게 할 수 있는지는 명확하게 설명된게 없어서(혹은 제가 못찾아서-_-, 제가 몰라서-_-) 더 설명드리기는 조금 힘들거 같군요.


- 마치면서

확실히 제게는 조금 벅찬 주제일 수도 있지만, 제가 이해한 범위내에서 최대한 명쾌하게 설명드리려고 노력했습니다. 하지만 틀린부분이 있을 수 있겠죠. 그렇다면 여러분의 지식을 공유하시면서 따쓰한 피드백을 주시기 발합니다. 캬캬캬....


- 참고자료

1. http://blogs.msdn.com/hugunin/archive/2007/05/15/dlr-trees-part-1.aspx
2. http://channel9.msdn.com/pdc2008/TL10/

Welcome to Dynamic C#(7) - 아낌없이 표현해 주는 나무

C# 2009. 9. 12. 08:30 Posted by 알 수 없는 사용자

- 럭키 세븐 -_-v

기분도 좋게 일곱번째 글이 되는 오늘은 아낌없이 표현해주는 나무, expresion tree를 가지고 이야기 해보도록 하겠습니다. LINQ의 뒤를 든든하게 받치고 있는 요소지만, 전면에 거의 드러나지 않아서 이게 뭔지 알아보려고 노력안하면 볼일이 없는 친구입니다. 저 역시 LINQ로 프로젝트를 진행하면서도 얉은 지식과 호기심으로 인해서 이런게 있다는 것도 모르고 있었는데요. 그리고 6월에 있었던 세미나에서는 '컴파일러같은 툴 개발자들에게나 적합한 기능인거 같다'는 망언을 하기도 했었습니다. 뭐 100% 틀린 말은 아니겠지만, 이해가 부족한 탓에 나온 망언이었던거니 혹시 마음상한 분 있으셨다면 여친한테 밴드라도 붙여달라고 하시면 좋을거 같네요. 여친없으시면 어머니한테라도...


- 표현해주는 나무나 빨랑 설명해봐

일단 expression tree는 실행가능한 코드를 데이터로 변환가능한 방법을 제공해주는 요소입니다. 이렇게 데이터로 변환하게 되면 컴파일하기 전에 코드를 변경한다거나 하는 일이 매우 수월해지는데요. 예를 들면, C#의 LINQ 쿼리식을 sql 데이터베이스같이 다른 프로세스상에서 수행하는 코드로 변환하는 경우 말이죠.

Func<int, int, int> function = (a, b) => a + b;

위와 같은 문장은 머리,가슴,배는 아니지만 세부분으로 구성됩니다.

1. 선언부 : Func<int, int, int> function
2. 등호 연산자 : =
3. 람다 식 : (a, b) => a + b;

현재 변수 function은 두 숫자를 받아서 어떻게 더하는지를 나타내는 코드를 참조하고 있습니다. 그리고 위의 람다 표현식을 메서드로 표현해본다면 대략 아래와 같은 모양이 되겠죠.

public int function(int a, int b)
{
    return a + b;
}

Func는 System네임스페이스에 아래와 같이 선언되어 있습니다.

public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); 

이 선언이 우리가 두개의 숫자를 더하는 간단한 람다식을 선언하는 걸 도와준 셈이죠.


- 근데, 어케 코드에서 표현해주는 나무로...?

위에서 우리는 실행가능한 코드를 function이라는 변수를 통해서 참조할 수 있다는걸 봤습니다. 근데, expression tree는 실행가능한 코드나 아니고, 자료구조의 한 형태입니다. 그러면, 어떻게 표현식을(코드를) expression tree로 변환하는 걸까요? 그래서 LINQ가 좋은걸 준비해뒀습니다.

using System.Linq.Expressions;

....

Expression<Func<int, int, int>> expression = (a, b) => a + b; 

위와 같이만 하면, 람다식이 expression tree로 쑉~! 하고 변환이 됩니다. 그럼 이걸 좀 더 눈에 보이게 살펴볼까요? 여기에 가시면, C#으로 구현된 예제들을 받을 수 있는데요, 예제중에 ExpressionTreeVisualizer(이하, ETV)라는게 있습니다. expression tree를 TreeView컨트롤을 이용해서 보기쉽게 쪼개주는 놈이죠. (a, b) => a + b;를 한번 확인해볼까요?



위의 간단한 예제코드는, Expression<TDelegate>클래스를 사용하고 있는데요, 그 클래스의 4가지 프로퍼티를 ETV를 통해서 확인해보실 수 있습니다. 좀 더 확실하게 하기 위해서 '-'를 눌러서 다 접어볼까요?



위 그림을 보시면, 딱 4가지만 남아있죠?

  • Body : expression의 몸체를 리턴한다.
  • Parameters : 람다식의 파라미터를 리턴한다.
  • NodeType : expression tree의 특정노드의 ExpressionType을 리턴한다. ExpressionType은 45가지의 값을 가진 열거형타입인데, expression tree에 속할 수 있는 모든 노드의 목록이 포함되어 있다. 예를 들면, 상수를 리턴하거나, 파라미터를 리턴한다거나, 둘 중에 뭐가 더 큰지 결정한다거나(<,>), 두 값을 더한다거나(+) 하는 것들이 있다.
  • Type : expression의 정적인 타입을 리턴한다. 위의 예제같은 경우에는 Func<int, int, int>가 되겠다.

아래와 같은 코드를 통해서 위의 프로퍼티들의 값을 확인해볼 수 있습니다.

using System;
using System.Linq.Expressions;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int, int>> expr = (a, b) => a + b;

            BinaryExpression body = (BinaryExpression)expr.Body;
            ParameterExpression left = (ParameterExpression)body.Left;
            ParameterExpression right = (ParameterExpression)body.Right;

            Console.WriteLine(expr.Body);
            Console.WriteLine("표현식의 왼쪽 : {0}\n노드의 타입 : {1}"
                + "\n표현식의 오른쪽 : {2}\n몸체의 타입 : {3}",
                left.Name, body.NodeType, right.Name, body.Type);
        }
    }
}


그리고 실행결과는 아래와 같습니다.



결과에서 expression의 모든 요소들이 각각의 노드로 이루어진 자료구조라는 것을 확인해보실 수 있습니다. 그리고 반대로 expression tree를 코드로 변환해서 실행하는 것도 매우 간단합니다. 아래와 같이 딴 한줄이면 됩니다.

int result = expr.Compile() (3,5); 


- 근데 당췌 왜 코드를 나무로 바꾸는건데? 식목일이냥?

이제 expression tree에 대해서는 조금 익숙해지셨을 겁니다. 특히 이 expression tree가 LINQ to SQL에 아주 중요한 역할을 하는데요, 일단 아래의 LINQ to SQL 쿼리문을 보시져~.

var query = from u in db.Users
     where u.nickname == "boram"
     select new { u.uId, u.nickname };


위 쿼리문의 결과로 반환되는 타입을 확인해보면 아래와 같습니다.



넵 바로 IQueryable인데요, IQueryable의 정의를 확인해보면 아래와 같습니다.

public interface IQueryable : IEnumerable
{
    Type ElementType { get; }
    Expression Expression { get; }
    IQueryProvider Provider { get; }

즉, 멤버로 Expression타입의 프로퍼티를 가지고 있습니다. IQueryable의 인스턴스는 expression tree를 가지고 있도록 설계된 거죠. 그 expressio tree가 바로 코드로 작성한 LINQ쿼리문의 자료구조입니다. 그런데, 왜 이렇게 LINQ to SQL쿼리를 expression tree형태로 가지고 있는걸까요? 그 핵심은, A라는 프로그램의 코드에 LINQ to SQL쿼리가 있다고 했을때, 실제로 이 쿼리가 수행되는 곳이 A가 아니라 데이터베이스 서버라는 점에 있습니다. 즉, 프로그램에서 직접실행되는게 아니라 데이터베이스가 알아들을 수 있는 SQL쿼리형태로 변환을 해서, 그 쿼리를 데이터베이스에게 날려서 쿼리된 데이터를 받아온다는 거죠. 위의 LINQ to SQL쿼리는 대략 아래와 같은 SQL문으로 변환이 됩니다.

SELECT [t0].[uId], [t0].[nickname]
FROM [dbo].[Users] AS [t0]
WHERE [t0].[nickname] = @p0 

프로그램내에 존재하는 쿼리표현식은 SQL 쿼리로 변환되어서 다른 프로세스에서 사용되게끔 문자열 형태로 보낸다는 거죠. 그러면, 위에서 설명드린대로 실제 쿼리는 데이터베이스의 프로세스내에서 처리가 되구요. 즉, IL코드를 SQL쿼리로 변환하는 거 보다, expression tree같은 자료구조형태가 변환하기 훨씬 쉬울뿐더러, 최적화 같은 중간처리도 훨씬 용이하다는 겁니다. 하지만 LINQ to Objects를 통해서 쿼리를 해보면 결과의 타입은 IEnumerable<T>입니다. 왜 얘네들은 IQueryable<T>가 아닐까요? IEnumerable<T>의 정의를 보면 아래와 같습니다.

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();

즉, Expression타입의 프로퍼티가 없습니다. 왜냐면, LINQ eo Objects는 같은 프로세스내에서 처리될 객체들을 대상으로 쿼리를 하기 때문에 다른 형태로 변환될 필요가 없기 때문입니다. 그렇다면, 굳이 expression tree같은 자료구조로 변환할 필요가 없겠죠. 그래서 대략 아래와 같은 규칙이 성립합니다.
  • 코드가 같은 프로그램(또는 프로세스)내에서 실행되는 경우라면 IEnumerable<T>
  • 쿼리 표현식을 다른 프로그램(또는 프로세스)에서 처리하기 위해서 문자열 형태로 변환해야 한다면 expression tree를 포함하는 IQueryable<T>를 사용 


- 마치면서

일단 오늘은 C# 3.0에 포함되었던 expression tree에 대해서 설명을 드렸습니다. DLR이랑 dynamic이야기 하다가 난데없이 삼천포로 빠진 느낌이 드시겠지만(저는 경남 진주에 살았었는데, 아버지 따라 삼천포 자주 갔었습니다....이 이야기는 왜하는 거지..-_-), DLR에서 expression tree가 중요하게 사용되고, 또한 C# 3.0을 사용해보신 분들이라도 expression tree에 대해서 제대로 못짚고 넘어간 분들도 많으리라 생각합니다. 저 역시 그랬구요. 아무튼, 도움되셨기를 바라면서 다음에 뵙죠!


- 참고자료

1. http://blogs.msdn.com/charlie/archive/2008/01/31/expression-tree-basics.aspx

Welcome to Dynamic C#(6) - Return to Dynamic (2)

C# 2009. 9. 3. 13:36 Posted by 알 수 없는 사용자

- 복습.

지난시간에서 이야기 했던 부분을 조금 이어서 이야기하자면요, dynamic이라는 타입이 생겼고 이 타입은 런타임에 가서야 실제로 담고있는 타입이 뭔지 수행하고자 했던 연산이 존재하는지 등을 알 수 있습니다. 그리고 닷넷 프레임워크 내부적으로는 dynamic이라는 타입은 없으며, object타입에 dynamic 어트리뷰트가 붙어서 런타임에게 적절한 동적 연산을 수행하도록 알려주도록 하고 있었습니다. 그리고 dynamic과 관계된 연산을 만나면, 컴파일러는 DLR의 call site를 이용하는 코드를 생성하구요 DLR에 정의된 기본연산들중에 C#에 알맞도록 상속된 클래스를 생성하고 그 연산을 call site를 통해서 호출하도록 코드를 생성해줍니다.

그러면 잠깐 실제로 동적연산을 호출하면 런타임에 어떤 절차를 거치게 되는지 알아보도록 하겠습니다.


- 복습은 거기까지.

dynamic d = ......;
d.Foo(1, 2, d); 

위와 같은 코드가 실행된다고 할때 대략 아래와 같은 절차를 따르게 됩니다.

1. DLR이 사용자가 넘겨주는 매개변수의 타입(int, int, dynamic)으로 요청받은 액션(InvokeMember)이 캐시되어 있는지 확인합니다. 캐시에 저장이 되어 있다면, 캐시되어있던 걸 리턴합니다.

2. 캐시에 해당되는 내용이 없다면, DLR은 액션을 요청받는 객체가 IDynamicObject(이하 IDO)인지 확인합니다. 이 객체는 스스로 어떻게 동적으로 바인딩하는지를 알고 있는 객체입니다.(COM IDispatch object, 루비나 파이썬의 객체나 IDynamicObject 인터페이스를 구현한 닷넷 객체들 처럼). 만약 IDO라면 DLR은 IDO에게 해당 액션을 바인딩해달라고 요청합니다. IDO에게 바인딩을 요청한 뒤에 받는 결과는 바인딩의 결과를 나타내주는 expression tree입니다.

3. IDO가 아니라면, DLR은 language binder(C#의 경우는 C# runtime binder)에게 해당 액션을 바인딩해줄 것을 요청합니다. 그러면 C# runtime binder가 그 액션을 바인딩하고 바인딩의 결과를 expression tree로 리턴해줍니다.

4. 2번이나 3번이 수행된 다음엔 결과로 받은 expression tree가 DLR의 캐시속으로 통합되고 같은 형태의 요청이 다시 들어온다면 바인딩을 위한 절차를 수행하지 않고 캐시에 저장된 결과를 가지고 실행하게 됩니다.


그리고 이어서 C# runtime binder에 대해서 소개해 드릴텐데요, C# runtime binder는 무엇을 어디에 바인딩할지를 결정하는 심볼테이블을 reflection을 이용해서 생성합니다. 만약에 컴파일타임에 타입이 정해진 매개변수라면 C# 액션의 정보에 해당 타입으로 기록되고 런타임시에 바인딩될때 그 매개변수는 기록된 타입으로 사용될 수 있겠죠. 근데 만약에 컴파일 타임에 dynamic으로 결정된 매개변수라면(dynamic타입의 변수나, dynamic을 리턴하는 표현식), runtime binder는 reflection을 이용해서 그 매개변수의 타입을 알아내고, 알아낸 타입을 그 매개변수의 타입으로 사용하게 됩니다.

- 심볼테이블?

심볼테이블은 컴파일러나 인터프리터가 코드를 번역하기 위해서 사용하는 자료구조인데요. 코드의 변수명, 메서드등의 식별자를 각각 타입이나 범위, 그리고 메모리에서의 위치등을 기록합니다. 아래의 표는 위키피디아에 있는 심볼테이블의 예제인데요, 메모리의 주소와 심볼의 타입과 심볼의 이름으로 구성되어 있습니다. 

Address Type Name
00000020 a T_BIT
00000040 a F_BIT
00000080 a I_BIT
20000004 t irqvec
20000008 t fiqvec
2000000c t InitReset
20000018 T _main
20000024 t End
20000030 T AT91F_US3_CfgPIO_useB
2000005c t AT91F_PIO_CfgPeriph
200000b0 T main
20000120 T AT91F_DBGU_Printk
20000190 t AT91F_US_TxReady



runtime binder는 심볼테이블을 필요할때 필요한 만큼 만드는데요, 위의 짧은 예제에서 처럼 Foo라는 메서드를 호출하는 경우라면 runtime binder는 d의 런타임 타입에 대해서 Foo라는 이름을 가진 멤버들을 모두 로드합니다. 그리고 형변환역시 요구되는 형변환들을 모두 심볼테이블로 로드합니다. 그리고 런타임 바인더는 C# 컴파일러가 하는 것과 동일한 오버로딩 판별알고리즘을 수행합니다. 그리고 컴파일시에 받는 것과 동일한 문법을 사용하며, 동일한 에러, 동일한 예외를 출력합니다. 그리고 마지막으로 이런과정을 거친 결과를 expression tree로 생성하고 DLR에게 리턴해줍니다. 단, 여기서 사용하는 expression tree는 C# 3.0의 expression tree를 확장한 것입니다. 기존의 expression tree에 동적인 연산, 변수, 이름 바인딩, 흐름제어를 위한 노드들이 추가된 거죠.


- 마치면서

뭔가, 짧고 설명도 어색한 포스트인거 같네요;;; 이번시간을 통해서 DLR이 코드를 실행하기 위해서는 코드를 내부적으로 expression tree라는 형태로 가지고 있음을 알아봤습니다. 다음시간엔 C# 3.0에서 처음등장한 expression tree와 DLR에서 사용하는 expression tree에 대해서 설명을 드리도록 하겠습니다.


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/10/29/dynamic-in-c.aspx
2. http://en.wikipedia.org/wiki/Symbol_table

Welcome to Dynamic C#(5) - Return to Dynamic.

C# 2009. 8. 26. 15:02 Posted by 알 수 없는 사용자

- 그때 너 너무 대충하더라

제가 맨 처음 dynamic에 대한 포스트로 Welcome to Dynamic시리즈를 시작했는데요. 뭐랄까 너무 추상적인 내용위주로 진행했다는 생각이 들더군요. 그리고 공부를 더 하다보니 그런 생각이 더 확실해지더군요. 없는 input에서 output이 나올수는 없는거니 당연한 이야기 겠지요. 캬캬캬. 앞으로는 dynamic과 DLR에 대해서 이야기를 조금 진행해보려고 합니다. 그래봤자 여전히 별 내용없거나 다른분들이 주는 insight를 그대로 전해주는 역할 이상은 못할지도 모르지만 일단 늘 그래왔듯이 노력해보겠습니다. 따쓰한 피드백을. ㅋㅋㅋㅋ


- dynamic?

dynamic d = ....;
d.Foo();

여기서 지역변수인 d는 dynamic타입을 가집니다. dynamic은 엄연히 컴파일러가 지원하는 타입이고, 타입이름이 들어갈 수 있는 곳에는 어디든지 dynamic이라고 명시해줄 수 있습니다. 즉, 실제타입이 동적으로 결정되는 것을 의미하는 정적인 타입인거죠. 차이점이라고 한다면, Foo라는 메서드를 호출하는 IL코드를 바로 만든다기 보다는 DLR과 C# 런타임 바인더를 통해서 dynamic call site라는 걸 호출되는 지점에서 생성합니다. 

이런 기능을 통해서 여러분이 기존에 써오던 친숙한 방법과 모습으로 파이썬이나 루비, 혹은 "스스로 어떻게 실행해야 하는지 알고있는" 객체들을 사용할 수 있게 해줍니다. 개인적으로는 이 부분이 꽤나 중요한 부분이라고 생각합니다. 다른 동적언어들이 있는데, 굳이 C#에 이런 기능이 들어가는건 앤더스 헬스버그의 철학답게, 기존에 잘 사용해오던 언어에 새로운 기능을 잘 통합시켜서 한 부분에만 특화된 언어보다 기존의 언어에서도 새로운 기능을 사용할 수 있게 해주는 거겠죠. 그래서 기존에 잘 사용해오던 언어가 새로운 요구에 발맞추는 새로운 표현법을 계속해서 잘 통합시켜 나가면서 생명력을 유지할 수 있게 말이죠.

위에서 말씀드렸듯이 dynamic은 분명히 존재하는 타입이고 컴파일러도 잘 알아듣는 타입이지만, 현재까지의 모습으로 봤을땐 실제로는 존재하지 않는 타입입니다. DLR과의 연동을 통해서 가능한 동적인 프로그래밍을 문법적으로 편리하게 만들어주는 syntatic sugar같은 역할이라고 볼 수 있을까요? 일단 아래코드를 보시져.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication2
{
    class Program
    {
        public dynamic DynamicCall(dynamic d)
        {
            object obj = 5;
            return d.Foo();
        }

        static void Main(string[] args)
        {           
        }
    }
}


그리고 이 코드에서 타입위에 마우스를 가져가 보시져. object와 dynamic을 비교해보겠습니다.




object위에 마우스를 올렸을때는 "class System.Object"라고 나오는데, dynamic에는 그런 표시가 없죠? 그럼 우리의 심증을 물증으로 굳혀보겠습니다. 위의 코드를 컴파일한 코드를 리플렉터에서 보면 아래와 같습니다.

----- 리스트 1 -----

[return: Dynamic]
public object DynamicCall([Dynamic] object d)
{
    if (<DynamicCall>o__SiteContainer0.<>p__Site1 == null)
    {
        <DynamicCall>o__SiteContainer0.<>p__Site1 =
            CallSite<Func<CallSite, object, object>>.Create(
                new CSharpInvokeMemberBinder(
                    CSharpCallFlags.None, "Foo", typeof(Program), null,
                    new CSharpArgumentInfo[] {
                        new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
    }

    return <DynamicCall>o__SiteContainer0.
                    <>p__Site1.Target(<DynamicCall>o__SiteContainer0.<>p__Site1, d);
}

dynamic이라는 타입은 싹 사라지고 object만 덩그러니 있는걸 확인할 수 있습니다. 그리고 dynamic이라고 알려주는 지시자같은게 붙어있는걸 보실 수 있습니다. 즉 beta1기준으로 현재에는 dynamic은 엄연히 하나의 타입이지만, 닷넷 프레임워크 내부적으로는 dynamic이라는 타입이 존재하지 않는다는 말입니다. 즉, 저렇게 dynamic이라고 표시가 된 객체는 컴파일러가 런타임에게 동적으로 처리되어야 한다는 걸 알려주는게 되겠죠.

그리고 위에서 말씀드렸듯이 컴파일러는 dynamic과 관계된 연산을 만나게 되면, DLR을 통해서 DLR의 call site를 이용하는 코드를 생성합니다. 코드에 보시면 'SiteContainer', 'CallSite'같은게 보이시죠? DLR에 기반한 동적언어에서 어떤 동적 연산을 호출하면 그 코드는 DLR이 이해할 수 있는 기본적인 연산으로 번역되고, 그 기본적인 연산을 적용할 대상에 따라서 Python객체에 하려면 Python Binder로, 기본적인 .NET 객체에는 Object Binder로, C#은 C#런타임 바인더로 적용하게 됩니다. 그 기본적인 연산의 목록은 현재 아래와 같습니다.

----- 리스트 2 -----

namespace System.Dynamic
{
    public class DynamicObject : IDynamicMetaObjectProvider
    {
        protected DynamicObject();

        public virtual IEnumerable<string> GetDynamicMemberNames();

        public virtual DynamicMetaObject GetMetaObject(Expression parameter);

        public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result);

        public virtual bool TryConvert(ConvertBinder binder, out object result);

        public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result);

        public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes);

        public virtual bool TryDeleteMember(DeleteMemberBinder binder);

        public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result);

        public virtual bool TryGetMember(GetMemberBinder binder, out object result);

        public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result);

        public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);

        public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value);

        public virtual bool TrySetMember(SetMemberBinder binder, object value);

        public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
    }
}


위의 목록에서 보시면 TryInvokeMember메서드가 있고 인자로는 InvokeMemberBinder를 받는게 보이시져? 그리고 위의 리플렉터에서 뽑은 리스트1을 보시면, Create메서드의 인자로 CSharpInvokeMemberBinder를 생성하고 있습니다. 그리고 CSharpInvokeMemberBinder를 따라가보면, base클래스가 InvokeMemberBinder가 나옵니다. 즉, DLR이 이해할 수 있는 기본연산이 C# 바인더를 통해서 실행되고 있을음 유추해볼 수 있습니다.

그리고 리스트1에서 "<>p_Site1" 이라는 걸 따라가보면 선언이 아래와 같습니다.

public static CallSite<Func<CallSite, object, object>> <>p__Site1;

즉, static 필드인데요. 델리게이트도 담고 있습니다. 이게 Foo메서드 호출에 대한 dynamic call site를 가지고 있는 필드입니다. 위의 리스트1의 코드를 보시면, 이 <>p_Site1에 저장된 내용을 Target메서드를 통해서 호출하고 있는 모습을 보실 수 있습니다. 생긴게 좀 복잡하긴 한데요, 이걸 뭐 직접 짜야하는건 아니고 컴파일러가 작업해주는 거니까요. 이 이야기는 나중에 더 자세하게 다루도록 하구요. 일단 dynamic이 어디에 어떤 모습으로 쓰일 수 있고, 그게 뭘 의미하는지 더 알아보도록 하겠습니다. 우선, 아래의 코드를 보시져. 

dynamic d = ...;

d.Foo(1, 2, 3); // (1)

d.Prop = 10; // (2)

var x = d + 10; // (3)

int y = d; // (4)

string y = (string)d; // (5)

Console.WriteLine(d); // (6)
(d.Foo(); 에서 d는 Foo의 실행요청을 받는 receiver이고, d의 타입이 dynamic이라면 d는 dynamic receiver가 되는거임!)

1. Foo메서드의 호출요청을 받은 객체의 타입이 dynamic이므로 컴파일러는 런타임에게 이 코드에서 d의 실제 runtime type이 뭐든지에 상관없이 "Foo"라는 메서드를 매개변수{1, 2, 3}를 적용해서 바인드해야 한다는걸 알려줍니다.

2. 역시 1번과 마찬가지로 dynamic receiver가 있으므로 컴파일러는 런타임에게 이 코드에서는 "Prop"이라는 프로퍼티비스무리한(필드나 프로퍼티)걸 바인드해야 하고 거기에 10이라는 값을 할당해야 한다고 알려줍니다.

3. 여기서는 +연산자는 동적으로 바인드되는 연산인데요, 매개변수중에 하나가 dynamic이기 때문이죠. 런타임은 실제 d의 runtime type에 대해서 일반적인 연산자 오버로딩 규칙을 따라서 적합한 연산을 찾습니다.

4. 여기서는 암시적인 형변환이 있는데요, 컴파일러는 int와 d의 runtime type에 대한 모든 형변환을 고려해본뒤에 d에서 int로의 형변환이 가능한지 판단하도록 런타임에게 알려줍니다.

5. 이번에는 명시적인 형변환 인데요, 컴파일러는 이 변환을 컴파일하고 런타임에게 이 형변환에대해서 검토해보도록 알려줍니다.

6. 비록 컴파일타임에서 볼 수 있는 메서드를 호출하지만, 인자가 dynamic이므로 컴파일타임에서는 오버로딩 판별을 할 수 없습니다. 그래서 어떤 Console.WriteLine을 호출할지도 역시 런타임에 결정하게 됩니다.


- 마치면서

오늘은 dynamic타입에 대해서 이야기 해봤습니다. 재주가 부족해서 잘 설명한거 같지 않네요;;; 생각보다 dynamic배후의 이야기가 많은데요, 다음시간부터 거기에 대해서 하나씩 하나씩 이야기 해보겠습니다~.


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/10/29/dynamic-in-c.aspx
2. http://blogs.msdn.com/cburrows/archive/2008/10/27/c-dynamic.aspx
3. http://channel9.msdn.com/pdc2008/TL10/

Welcome to Dynamic C#(4) - 극과극 비교체험.

C# 2009. 8. 20. 20:25 Posted by 알 수 없는 사용자

- 또 쓸데없는 생각 하냐?

안녕하세요. 정말 오랜만입니다. 사연이 많은 사람이다 보니, 잠수를 자주 타게 되네열. -_-;;;;; 그래서 뭐라도 써야한다는 생각을 하다가, 별로 쓸모있을진 모르겠지만, 실행속도를 비교해보자는 생각이 들었습니다. 짧은 글이 되겠지만, 조금이라도 도움이 되길바라면숴!


- 빨랑 비교한거 내놔.

비교대상은 한 클래스에 있는 메서드를 그냥 호출하는 것과 dynamic을 통해 호출하는 것, 그리고 리플렉션을 통해서 호출하는 세가지방법입니다. 그리고 각 호출을 메서드를 10만, 50만, 100만, 300만, 500만번 호출하는 것으로 속도를 재어봤습니다. 실행의 대상이 된 코드는 아래와 같습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication2
{
    class Test
    {
        public int FivePlusFive()
        {
            return 10;
        }
    }

    class Program
    {
        public void ReflectionCall()
        {
            object test = new Test();
            Type type = test.GetType();
            type.InvokeMember("FivePlusFive", System.Reflection.BindingFlags.InvokeMethod, (Binder)null,
                test, new object[] {});
        }

        public void DynamicCall()
        {
            dynamic test = new Test();
            test.FivePlusFive();
        }

        public void NormalCall()
        {
            Test test = new Test();
            test.FivePlusFive();
        }

        static void Main(string[] args)
        {
            Program prog = new Program();

            //for JIT compile
            prog.ReflectionCall();
            prog.DynamicCall();
            prog.NormalCall();

            long limit = 5000000;

            DateTime normalStart = DateTime.Now;
            for (int i = 0; i < limit; i++)
            {
                prog.NormalCall();
            }
            DateTime normalEnd = DateTime.Now;
            TimeSpan normalResult = normalEnd - normalStart;

            DateTime dynamicStart = DateTime.Now;
            for (int i = 0; i < limit; i++)
            {
                prog.DynamicCall();
            }
            DateTime dynamicEnd = DateTime.Now;
            TimeSpan dynamicResult = dynamicEnd - dynamicStart;

            DateTime reflectionStart = DateTime.Now;
            for (int i = 0; i < limit; i++)
            {
                prog.ReflectionCall();
            }
            DateTime reflectionEnd = DateTime.Now;
            TimeSpan reflectionResult = reflectionEnd - reflectionStart;

            Console.WriteLine("Normal Time : {0}", normalResult);
            Console.WriteLine("Dynamic Time : {0}", dynamicResult);
            Console.WriteLine("Reflection Time : {0}", reflectionResult);
        }
    }
}



JIT컴파일에 걸리는 시간을 빼기 위해서 일단 한번씩 먼저 실행했구요, 각각의 방법을 정해진 횟수만큼 실행해서 시간을 측정하는 방식으로 했습니다. 그럼 결과를 보시져!!!! 야호!!!! 완전 신나!!!! -_-......

- 10만번

- 50만번

- 100만번

- 300만번

- 500만번


그리고 위의 결과를 표로 종합해보면 아래와 같습니다.


일반 호출은 리플렉션에 비해서 너무 작아서 그런지 아예 나타나지도 않는군요-_-;;;; 별로의미있는 코드로 실험을 한건 아니지만, 다이나믹이 리플렉션에 비해서는 월등히 빠르군요. 아마도 DLR의 힘을 빌려서 리플렉션 보다 훨씬 빠른 방식을 이용하는 것 같습니다. 이 부분에 대해서는 좀 조사를 해봐야 할거 같네요.


- 피드백 및 정정사항!!ㅋ

이 글을 보시고 정성태님께서 피드백을 주셨습니다.(http://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&detail=1&pageno=0&wid=766&rssMode=1&wtype=0) 정성태님의 블로그를 들르면서 내공의 깊이에 감탄을 하곤했는데, 직접 피드백을 받으니 더 확실하네 느껴지네요^^ㅋ. 글의 내용을 보시면, 제가 단순히 리플렉션 호출을 반복하게 설정해놓은 것에서 리플렉션에 매우 불리한 결과가 도출되는 요인이 있음을 지적하시고, 더 빠르게 그리고 오히려 다이나믹 보다도 더 빠른 결과가 나올 수 있는 방식을 제시해주고 계십니다.

잘 몰랐던 부분에 대해서 지적해주셔서 좋은거 배웠네요~.


- 마치면서

별로 내용도 없는 글을 썼군요-_-;;; 다음 포스트부터는 dynamic에 대해서 좀 더 심도 깊게 파보려고 생각중입니다. 좋은글이 많은데 잠수타고 정신줄 놓느라고 못보고 있었더군요!! 암튼. 곧 돌아오겠슴돠. ㅋㅋㅋ

Welcome to F#(12) - 공동작업 좋치아니항가

F# 2009. 7. 16. 14:38 Posted by 알 수 없는 사용자

- F#의 첫 라이브러리 출연(Feat. C#)

오늘은 간단하게, F#에서 만든 코드를 C#에서 사용해보는 시간을 갖겠습니다. 자~ 간단하게 간단하게~.

우선, C#으로 콘솔프로그래밍 프로젝트를 하나 생성하고 같은 솔루션에 F# 라이브러리 프로젝트를 하나 추가합니다. 대략 아래와 같은 모양이 됩니다. 



그리고 Module1.fs에다가 아래의 코드를 작성합니다. 


 module FirstModule =
    type Type1 =
        { num: int }
        member self.reverse = { num = -self.num }
        member self.add(num2) = { num = self.num + num2 }
       
type Type2 =
    { num: int }
    member self.reverse = { num = -self.num }
    member self.add(num2) = { num = self.num + num2 }

let add5 num = num + 5



FirstModule라는 모듈을 선언하고 그 안에 Type1이라는 타입을 선언한 걸 보여주고 있습니다. 그리고 Type1의 멤버로는 int타입의 num이 있고 reverse, add라는 함수가 있습니다. 각각의 함수뒤의 {}안의 코드는 num에 새로운 값을 할당해서 Type1의 새로운 객체를 리턴하는 내용으로 보시면 이해가 되실 겁니다. 그리고 그 아래에는 모듈선언없이 그냥 Type2라는 타입을 선언했구요, 그 밑에는 함수 add5를 선언했습니다. 이런 코드들을 C#에서 참조한다면 어떻게 쓸 수 있을까요? 

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CoWorkWithFSharp
{
    class Program
    {
        static void Main(string[] args)
        {
            Module1.FirstModule.Type1 type1 = new Module1.FirstModule.Type1(5);
            Module1.FirstModule.Type1 type1_2 = type1.reverse;

            Console.WriteLine(type1_2.num);

            Module1.Type2 type2 = new Module1.Type2(6);
            Module1.Type2 type2_2 = type2.add(9);

            Console.WriteLine(type2_2.num);

            Console.WriteLine(Module1.add5(12));
        }
    }
}



위 코드를 보시면, Module1이라는 파일이름이 가장 먼저 오는 걸 볼 수 있습니다. 파일이름자체가 모듈이 되는거죠. 베타1이라서 그런지는 모르겠지만, F#으로 만든 코드는 "using Module1;" 형태로 using을 할 수 없었습니다. 에러메세지를 보면 네임스페이스가 아니라서 안된다고 하니 모듈도 클래스로 취급이 되는거 같은데 개선이 될지 아니면, 이대로 남을지 모르겠네요. 코드를 보시면 알겠지만, 매번 모듈명을 반복해서 적어줘야 하니 좀 빡세긴 합니다만;;;; 

아무튼, 저렇게 F#에서 작성한 타입의 객체를 생성할 수 있구요, 멤버 메서드와 함수를 호출할 수도 있습니다. 다만, 함수의 결과로 새로운 객체가 생성된다는 점 기억하시구요. 실행결과는 아래와 같습니다. 



그러면, 지난주에 만들었던 Discriminated Union을 이용한 간단한 사칙연산 코드를 C#에서 사용해보도록 하겠습니다. 아래코드를 Module1.fs에 추가하구요 

type Expr =
    | Num of float
    | Add of Expr * Expr
    | Sub of Expr * Expr
    | Mul of Expr * Expr
    | Div of Expr * Expr

let rec Calc expr =
    match expr with
    | Num num -> num
    | Add (e1, e2) -> (Calc e1) + (Calc e2)
    | Sub (e1, e2) -> (Calc e1) - (Calc e2)
    | Mul (e1, e2) -> (Calc e1) * (Calc e2)
    | Div (e1, e2) -> (Calc e1) / (Calc e2)



아래 코드를 C#코드에 추가해줍니다. 

 Module1.Expr expr = new Module1.Expr._Mul(
    Module1.Expr.Add(
        Module1.Expr.Num(5.0),
        Module1.Expr.Num(6.0)),
        Module1.Expr.Div(
            Module1.Expr.Num(5.0),
            Module1.Expr.Num(2.0)));

Console.WriteLine(Module1.Calc(expr));



위 코드를 보시면, 지난주에 계산을 위해서 만들었던 식을 그대로 C#에서 생성했습니다. 실행결과는 아래와 같습니다. 




-정리하며

아~ 간단한 포스트였군요~. 대략 F#에서 만든 코드를 이런식으로 C#등의 다른 언어에서 호출해서 사용할 수 있습니다. 어느덧 베타1이 나온지도 꽤 됀 느낌이네요. 많이들 익숙해지셨나요? VS2008에서 뭔가를 만들다가 C#4.0의 dynamic을 쓰면 딱이겠다고 생각하는 순간, 아쉬움이 느껴지더군요. 아직 F#은 그만큼 익숙하지 못해서 그런경우는 없었던거 같네요-_-;;; 언제쯤 F#의 Jedi가 될 수 있을까요? ㅋㅋㅋ.


-참고자료

1. Expert F#, Don Syme, Adam Granicz,Antonio Cisternino, Apress

지난 6월 10일 VSTS 2010 팀에서 세미나를 진행하였습니다. 세미나 프레젠테이션은 MEF 세미나 자료 에서 볼 수 있습니다.

그리고 얼마 전에 촬영한 동영상도 공개가 되었습니다. VSTS 2010 은 굉장히 큰 규모의 개발 도구, 개발 플랫폼 등의 버전 업으로 아직도 많은 부분을 알려드리지 못했고, 미처 저희들도 모두 알지 못하는 부분도 많습니다.

하지만 남들보다 먼저 접해본 분야이고 이것을 알려드리기 위해 진행한 세미나입니다. 아래의 동영상을 시청하시고 VSTS 2010 에 많은 관심을 가져주세요. ^^

   

강보람 - C# 연대기 - C# 의 Before/After

   

공성의 - VSTS 2010 의 소프트웨어 품질 관리

   

김병진님의 - VSTS 2010 Architecture & UML

   

   

엄준일 ASP.NET MVP - Managed Extensibility Framework

   

최흥배 C++ MVP - Visual C++ 10, C++0x 그리고 Concurrency Runtime

   

[VSIX] 2-2. How to start VSIX programming

VIsual Studio Extensibility 2009. 6. 29. 18:47 Posted by 알 수 없는 사용자
Setting up the infrastructure for developing VSIX

개발을 시작하기 전 기본적인 인프라 구성을 먼저 하여 봅니다. 일단 Visual Studio 2010 을 다운로드 받아 설치해 보도록 하겠습니다.


친절하게도 이전에 슈러님께서 포스팅한 글이 있으므로 참고하면서 설치 하도록 하고, 이제 Visual Studio 2010 Beta 1 SDK를 설치 해 보도록 하겠습니다.

Installing Visual Studio 2010 SDK Beta 1


참고 링크를 따라 이동 하면 그림 1-1과 같은 Microsoft Download Center의 웹페이지가 나타날 것입니다. 거기서 Download 버튼 클릭 하면 Visual Studio 2010 SDK Beta 1을 다운로드 하실 수 있습니다. Release 날짜가 2009년 6월 12일로 다소 따끈 따끈한(?) 아이입니다.
Visual Studio 2010 SDK에는 Tool Windows, Menu Commands, Isolated Shell Projects, Editor Extension을 생성 할 수 있도록 프로젝트 템플릿을 제공하고 있으며, Editor Extension에는 Text Adorment, Colorizer, Magin 템플릿을 포함하고 있고, 빌드와 디버그 Extension은 개발자에게 VSX를 개발
                                                                           <그림 1-1>      하는데 큰 도움을 줍니다.

                                                                                                                         <그림 1-2>
그림 1-2는 초기 Visual Studio 2010 SDK Beta 1 Setup 화면입니다 Next버튼을 클릭하여 다음으로 넘어 갑니다. 그림 1-3과 같이 라이센스 동의 화면이 나타나는데 역시나 인스톨할 때 화면들이기 때문에 다음버튼만 죽어라 누르면 됩니다. 

                                                                                                                         <그림 1-3>
그리고 설치 경로를 물어보고 다음 버튼을 선택하면 설치를 시작합니다. 설치 시간은 대략 몇분이면 마무리 되므로 큰 인내심은 필요치 않습니다. 그리고 정확히 DSL 부분까지 파고 들어갈지 의문이기 때문에 만약 DSL도 다루게 된다면 그때가서 설치 부분을 포스팅 할지를 생각해 보도록 하겠습니다.

VSX를 개발하기 위한 물밑 작업은 완료 되었습니다. 참 쉽~죠잉~!
File > New > Project 를 클릭하면 그림 1-4와 같이 Extensibility 메뉴에 해당 Project Templete이 생성되어 있는 것을 확인 하실 수 있습니다.
                                                                                                                                    <그림 1-4>

다음 포스팅에서는 Editor Templete에 기능에 대하여 알아 보도록 하겠습니다.

Welcome to Dynamic C#(3) - 마음이 넒어진 C#

C# 2009. 6. 26. 08:58 Posted by 알 수 없는 사용자
- 대인배 모드

C#은 점점 더 마음이 넓어지고 있습니다. F#이라는 조금은 독특하지만 머리좋은 친구가 좋은 기능을 소개해주기도 하구요(C# 2.0의 Generics는 F#의 배후에 있는 Don Syme이 제안한거라는 군요), 팬들이 많아지면서 점점 더 넓은 마음을 갖추게 되었습니다. 그리고 대인배 모드에 또 중요한 역할을 해주는게 있는데요 Co- Contravariance입니다.

우선 그다지 친근하지 않은 이름이네요. 이건 대체 뭘까요? 일단 정리해서 말씀드리면, C# 3.0에서 불필요하게 캐스팅을 하면서 해야 했던 작업을 매우 편하게 할 수 있도록 도와주는 C# 4.0의 새로운 기능입니다. C# 3.0에서 못하게 막았던 부분중에서 문제를 일으킬 가능성이 없는 부분에 대해서는 편리하게 사용할 수 있도록 지원해준다는 말인데요. 뭔가 딱 듣기만 해도 대인배스러운 느낌이 들지 않나요 ㅋ. 무슨 이야기 인지 예제를 보면서 확인해보겠습니다. Eric Lippert의 기가막힌 글이 있는데요, 그 글을 참고로 하면서 작성한 글입니다.(링크는 아래 참고자료쪽에!) 


- 예제를 내놓지 않으면 구워먹겠다.

일단, 동물들에게 먹이를 주는 메서드를 통해 알아보도록 하지요. 아래와 같은 코드를 작성합니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Co_ContraVariance
{
    class Animal
    {
        public bool Hungry { get; set; }

        public override string ToString()
        {
            return "Animal";
        }
    }

    class Mammal : Animal
    {
        public override string ToString()
        {
            return "Mammal";
        }
    }

    class Giraffe : Mammal
    {
        public override string ToString()
        {
            return "Giraffe";
        }
    }

    class Tiger : Mammal
    {
        public override string ToString()
        {
            return "Tiger";
        }
    }

    class Program
    {
        void FeedAnimals(IEnumerable<Animal> animals)
        {
            foreach (Animal animal in animals)
            {
                if (animal.Hungry)
                {
                    Feed(animal);
                }
            }
        }

        private void Feed(Animal animal)
        {
            Console.WriteLine(animal.ToString());
        }

        static void Main(string[] args)
        {
            List<Giraffe> giraffes = new List<Giraffe>{
                new Giraffe{ Hungry = true},
                new Giraffe{Hungry = true},
                new Giraffe{Hungry = false}
            };

            Program program = new Program();
            program.FeedAnimals(giraffes);
        }
    }
}



딱히 잘못된 부분이 보이지 않는 코드입니다. 하지만, vs2008에서는 아래와 같은 에러가 납니다.

 


즉, 다른 타입이므로 메서드를 오버로드하라는 이야기지요. Giraffe는 Animal의 서브타입이므로 문제가 없습니다만, IEnumerable<Giraffe>는 IEnumerable<Animal>과 담고있는 요소의 타입이 다를뿐 어떤 관계도 없습니다. 그래서 IEnumerable<Giraffe>에서 IEnumerable<Animal>로는 형변환이 안되는 것이죠. 그래서 아래와 같은 코드를 통해 간접적으로 형변환을 해줘야 합니다.

program.FeedAnimals(giraffes.Cast<Animal>()); 


 
그런데, 안전한 경우에 한해서 이런게 잘 되게 해주겠다는 게 C#4.0의 이야기입니다. 일단 실행결과를 보시죠.

 

 

C#4.0으로 짠 코드는 잘 실행되는게 보이시죠? 그렇습니다. Covariance를 통해서 코드가 잘 실행된 것입니다. 그러면 이런 Covariance와 Contravariance에 대해서 좀 더 알아보죠.

일단, 간략하게 정의를 한번 내려보도록 하겠습니다.

우선 어떤 타입 T와 U가 있다고 했을때, 다음중 하나는 참이 됩니다.

1. T는 U보다 크다
2. T는 U보다 작다
3. T와 U는 같다
4. T와 U는 서로 관련이 없다.

위의 코드에서 보듯이, Giraffe는 Mammal의 서브타입이므로 Mammal보다 작습니다. 하지만, Giraffe는 Tiger와는 아무관련이 없는 타입이죠. 이런 타입간의 관계에서 어떤 연산을 T와 U에 대해서 수행했을때, 그 결과로 나온 T'와 U'가 T와 U의 관계를 그대로 유지한다면, 그연산은 covariant하다고 이야기 할 수 있으며, 대입의 방향성과 크고작음을 뒤집고 같음과 관계없음만 그대로 유지한다면(위 리스트에서 1,2번을 뒤집고 3,4번만 유지한다면) 이 연산은 contravariant하다고 이야기 할 수 있습니다.


잘 이해가 되시나요? 무슨 꼭 수학의 증명같은 냄새가 나서 거부반응이 일어나신 분들도 있을거 같군요. 그럼, 예제를 통해 차근차근 설명드리도록 하겠습니다.

 

 public interface IEnumerable<T> : IEnumerable
    {
        // 요약:
        //     Returns an enumerator that iterates through the collection.
        //
        // 반환 값:
        //     A System.Collections.Generic.IEnumerator<T> that can be used to iterate through
        //     the collection.
        IEnumerator<T> GetEnumerator();
    }

위 코드는 C#3.0의 IEnumerable의 정의인데요. 이 인터페이스에서 형식매개변수인 T는 GetEnumerator를 통해서 리턴될때만(output position에서만) 사용되는 걸 볼 수 있습니다. 즉, T의 요소를 편집할 방법이 없다는 이야기지요. 이런 경우는 형식매개변수의 대입의 방향성을 유지한 상태에서 IEnumerable<Giraffe>에서 IEnumerable<Animal>같은 형변환을 가능하게 해주겠다는 이야기 입니다.

 

- 대입의 방향성

Giraffe는 Animal의 서브타입입니다. 그렇다면 아래와 같은 코드가 가능하죠.

Animal animal = new Giraffe;

즉, Animal이 Giraffe보다 더 큰(상위) 타입이기 때문에 가능하죠. 하지만 아래의 코드는 컴파일되지 않습니다.

Giraffe giraffe = new Animal();

아래와 같이 하면 컴파일은 문제가 없습니다.

Giraffe giraffe = (Giraffe)new Animal();

하지만, 런타임에서 형변환이 불가능하다고 하면서 예외가 발생되죠. 즉, Animal이 더 크기 때문에, Giraffe는 Animal에 대입될 수 있지만, Animal은 Giraffe에 대입될 수 없습니다. 이게 크고작음에 관련된 대입의 방향성입니다.

단, Giraffe와 Tiger는 둘다 Mammal의 서브타입이지만, 둘사이에는 아무관련이 없습니다.



즉, 이야기를 이어 가자면 Giraffe가 Animal에 대입가능하기 때문에, 형식매개변수가 리턴될때만(output position에서) 사용된다면, 대입의 방향성을 유지한상태에서는 IEnumerable<Giraffe>에서 IEnumerable<Animal>로 변환을 하는것 같이 참조형변환을 지원해주겠다는 이야기 입니다.

 
반대로, Contravariance는 파라미터로 받기만하는 경우에만 사용할 수 있습니다. 그리고 대입의 방향성 역시 반대로 뒤집어 버립니다.

 public interface IComparer<T>
    {
        // 요약:
        //     Compares two objects and returns a value indicating whether one is less than,
        //     equal to, or greater than the other.
        //
        // 매개 변수:
        //   x:
        //     The first object to compare.
        //
        //   y:
        //     The second object to compare.
        //
        // 반환 값:
        //     Value Condition Less than zero x is less than y.  Zero x equals y.  Greater
        //     than zero x is greater than y.
        int Compare(T x, T y);
    }


위 코드는 특정타입에 대해서 비교를 지원해주고 싶을때 구현하는 인터페이스인데요, 여기도 역시 보시면, 형식매개변수인 T는 Compare메서드에서 비교할 인자를 받는 곳(input position)에서만 사용되는 걸 볼 수 있습니다. 예제를 위한 예제가 될 수 도 있지만, IComparer<T>를 통해서 contravariance를 설명해보죠~. 

위에서 작성한 코드에 아래와 같은 코드를 추가합니다. 

    class AnimalComparer : IComparer<Animal>
    {
        public int Compare(Animal x, Animal y)
        {
            return 1; //예제를 위해 ㅋ.
        }
    }

    class GiraffeComparer : IComparer<Giraffe>
    {
        public int Compare(Giraffe x, Giraffe y)
        {
            return 1; //예제를 위해 ㅋ.
        }
    }

.......중략.......

        static void Main(string[] args)
        {
            ........중략.......

            IComparer<Animal> animalComp = new AnimalComparer();
            IComparer<Giraffe> giraffeComp = animalComp;
            Console.WriteLine(giraffeComp.Compare(new Giraffe(), new Giraffe()));
        }



각각 IComparer<Animal>과 IComparer<Giraffe>를 구현한 비교 클래스입니다. 그리고 IComparer<Animal>을 IComparer<Giraffe>에 대입하고 있습니다. 그리고 아무문제없이 컴파일 됩니다. 그리고 실행도 문제없이 됩니다.



Animal은 Giraffe보다 더 큰 데 어떻게 IComparer<Giraffe>에 들어가는 걸까요? 그냥 상식적으로 생각했을때 동물을 비교할 수 있다면, 기린(giraffe)을 비교할 수도 있겠죠? 사람들이 IComparer<Giraffe>에 비교하려고 넣는 객체는 모두 Giraffe타입이겠고 giraffeComp가 실제로 호출하게 되는 Compare메서드는 IComparer<Animal>을 구현한 animalComp의 메서드겠죠. 그래서 방향성이 뒤집혔지만, contravariance로 인해서 실행가능한 코드가 됩니다.

반대로 IComparer<Animal>에다가 IComparer<Giraffe>를 대입하는 경우를 생각해보죠. 그렇다면 사람들은 animalComp를 통해서 Animal을 비교하고 싶어할텐데, 실제로 호출될 메서드는 IComparer<Giraffe>의 Compare메서드겠죠. 그 메서드는 Giraffe타입을 받을 수 있으므로 Animal을 받을 수 없습니다. 그래서 이런 참조형변환은 지원되지 않습니다.

즉, contravariance는 형식인자가 파라미터로 입력되는 부분에서만(input position에서만) 쓰일때, 대입의 방향성을 뒤집는 참조형변환을 지원해주겠다는 이야기 입니다. 일단은 여기까지가 co- contravariance에 대한 간략한 설명입니다. 


- 대인배 만쉐이 ㅋ

암튼, 점점 더 대인배가 되어가는 C#의 모습을 살짝 엿보았습니다. 어떻게 보면, 그냥 직관적으로 할 수 있는 부분들에 대해서 제약을 풀어준 셈이니까 대인배전략에 걸맞는 기능인 거 같습니다. 물론 마냥 낙관적인 것만은 아닙니다만. 성격좋은 사람도 나름의 규칙은 있는 법이니까요. 안전하고 적절할 co- contravariance의 사용은 "들어가는 거(형식매개변수)는 contravariant일 수 있고, 나가는 거(return되는)는 covariant일 수 있다."라는 군요. 그렇지 않은 경우도 있다는 말이겠죠. 제 내공이 허락한다면 이부분에 대해서도 정리해서 올리도록 하겠습니다. 대인배 C# 만쉐이 캬캬캬.

 

- 참고자료

1. http://blogs.msdn.com/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx
2. http://blogs.msdn.com/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx
3. http://blogs.msdn.com/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx
4. http://blogs.msdn.com/ericlippert/archive/2007/10/22/covariance-and-contravariance-in-c-part-four-real-delegate-variance.aspx
5. http://blogs.msdn.com/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-interface-variance.aspx
6. New features in C# 4.0, Mads Torgersen

[ASP.NET 4.0] 1. Core Service - Extensible Output Caching

ASP.NET 4.0 2009. 6. 24. 20:03 Posted by 알 수 없는 사용자
Evolution of ASP.NET

.NET Framework 4.0 beta 1와 함께 ASP.NET도 새로운 변화를 시도하였습니다. ASP.NET 2.0의 변화 보다는 덜 파격적이지만, 그 동안 .NET Framework 업데이트가 진행되는 동안 ASP.NET의 변화가 미미했던 것을 감안 한다면, 이번 .NET Framework 4.0 업데이트와 함께 동승한 ASP.NET의 업데이트는 놀라울 만큼 바뀌었습니다.(스캇 구스리 아저씨가 힘좀 쓰셨나 봅니다.ㅋㅋ)
그 중 Core Service 업데이트는 Performance 측면과 기존 ASP.NET Issue를 처리하는데 두드러진 발전을 이루어 냈습니다. 그럼, Core Service의 주요 업데이트 사항을 간략하게 소개하고 넘어가도록 하겠습니다.

1. Extensible Output Caching - 확장 가능한 출력 캐쉬 정도로 직역하겠습니다. 기존에도 있던 페이지 캐싱을 개발자 나름대로 커스트마이징 할 수 있도록 확장 시켜준 기능입니다.
2. Auto-Start Web Application - HTTP Request에 대하여 콜드 스타트 또는 개별 응용프로그램의 재사용에 소모되는 비용을 절감하기 위한 기능인 듯 합니다. 왜 ASP.NET 맨처음 띄우면 밥먹고 담배피고 와야 하잖아요.(오바입니다만...) 그런 비용을 절감 하기 위해 나온 기술인듯합니다. 허나, Windows Server 2008 R2에 탑재 가능한 IIS 7.5에서만 지원되는 기능이라니 OTL이 아닐 수 없습니다. 한 3년동안은 이 기술 볼 수 없을 듯합니다.(회사에서 2008을 깔아야 뭘 하죠? 아직 2000 쓰는 곳도 많이 있는데....)
3. Permanently Redirecting a Page - 영원히 헤어지는 이전 페이지와의 관계, 참 무슨 영화 제목 같 군요. 이전에 이슈가 되었던 Request가 여러번 일어나던 오래된 페이지에서 Redirect를 시킬 경우 HTTP 302 요청 이슈에 대한 대처 방안입니다. 이건 뭐 다들 아실만한 내용이겠네요.
4. The Incredible Shrinking Session State - 깜놀하게하는 세션 상태정보의 감소로 의역(?)하죠. ASP.NET 에서 단일 서버라면 모르지만, 웹팜(Web farm - 모르신다면 당장 찾아 보세요. 롸잇 놔우)에서는 세션 상태 정보를 저장하기 위해서 세션 State Server를 구성하거나 MS SQL을 이용하여 세션 정보를 저장할 수  있도록 저장소를 두는 방법이 있었습니다. 근데, 문제는 두 State Server 다 거의 Raw데이타에 가까운 정보들이 날(Raw)로 저장되어 엥간한 동시접속자 수를 자랑하는 사이트에서는 엄청난 데이터량이 문제였습니다. 데이터 양도 문제지만 Network Traffic도 데이터 양 비례하게 올라가는 문제를 양산하게 됐죠. 이를 해결하기 위해 Gzip Compression을 사용했다는 군요.
그럼, 본격적으로 Extensible Output Caching에 대해 알아 보도록 하겠습니다.



What is Extensible Output Caching?  

말 그대로 확장 가능한 출력 캐쉬 기능입니다. 쉽게 설명 하자면, 엄연히 Caching 기능은 ASP.NET 1.0부터 있었습니다. 페이지나 컨트롤, HTTP Response의 출력물(뭐 쉽게 말하자면 컴파일러에서 html 코드로 완전히 변환된 상태의 코드)을 서버의 메모리 내에 상주시킴으로써 재생산에 드는 비용을 감소 시키는 데에 일조 하던 기능입니다. 하지만, 이것도 만만찮게 다른 문제를 일으키고는 했는데요 저장소는 무조건 메모리로 박치기 하다보니, 다른 웹 어플리케이션과 메모리 선점 전쟁을 하게 되는 문제가 생기고, 서버 자체적으로는 쓰잘때기 없는 것(일명 쓸애기) 까지 메모리 영역을 점령함으로써 서버 리소스를 잡아먹게 되는 문제가 생긴 것이지요.

그래서, 기존에 있던 기능은 살리고~ 살리고~ 살리고 살리고 살리고 커스텀한 Provider만 Implement(구현)하여 대체 저장소를 선택할 수 있도록 한 기능입니다. 물론 MS SQL 서버에도 저장이 되도록 기능을 갖추고 있습니다.

그럼 실전으로 들어가 정확히 뭐하는 기능인지 코드로 살펴 보도록 하겠습니다.

Walkthrough : Step 1. Create a Web Project

웹 프로젝트를 그림 1-1 과 같이 선택하여 생성합니다.

<그림 1-1>

<그림 1-2>

자~! 여기까지는 많이 하셨을 테니 거두 절미 하고, 솔루션에 CustomOutputCacheProvider Class를 추가하기 위해 클래스 라이브러리 프로젝트를 생성하도록 하겠습니다.
                                                                                                                   <그림 1-3>
                                                                                                                                    <그림 1-4>

프로젝트를 생성하셨다면 TestCustomOutputCacheProvider 클래스를 추가 시킵니다. 그리고, CustomOutputCacheProvider 프로젝트에 System.Web과 System.Confihuration을 참조 추가 시키겠습니다. 이제 어느정도 구색이 갖추어졌습니다.

Walkthrough : Step 2. Implement OutputCacheProvider

이번 단계에서는 개발자 특정 저장소를 지정할 수 있도록 커스텀한 CacheProvider를 생성도록 하겠습니다.

<그림 1-5>

그림 1-5와 같이 System.Web.Caching.OutputCacheProvider 추상 클래스를 Implement할 경우 자동으로 오버라이드 메서드가 생성되는 것을 볼수 있습니다.
                                                                                                                           <그림 1-6>

기능 구현에 많은 시간을 할애 할 수 없는 관계로 간단하게 세션에다가 캐싱을 하도록 구현 하겠습니다. 저장소를 선택하고 구현 하는 부분은 각자 구미가 땡기는 대로 구현하여 보세요.

Walkthrough : Step 3. Setting WebSite

먼저 그림 1-7과 같이 Web.Config파일에 caching 섹션을 추가하고 Provider 섹션을 구성하도록 하겠습니다.
<그림 1-7>
 
여기서 부연 설명 드리겠습니다. <outputCache> 섹션의 Attribute인 defaultProvider는 Web Application에 기본적으로 제공할 OutputCacheProvider입니다. 나중에 나올 Global.asax에서 설정해 주지 않는 이상 자동적으로 AspNetInternalProvider를 제공합니다. 그리고 하위 섹션인 <providers> 섹션 이하 섹션에는 개발자가 커스텀 하게 만들어 놓은 Provider를 넣는 부분입니다. 복수 개의 CacheProvider를 제공할 수 있어 좀 더 성능 향상에 기여 할 수 있습니다.(점점 말투가 딱딱해 지고 있습니다. 퇴근 시간이 훌쩍 넘어서 인지 정신적 압박이...)

그리고나서, Global.asax를 추가 시켜보도록 하겠습니다. 그림 1-8과 같이 새로운 아이템 추가 버튼을 클릭하면 대화 상자에서 Global.asax파일을 선택하여 확인 버튼을 선택합니다.
                                                                                                     <그림 1-8>

추가된 Global.asax 파일을 열고 그림 1-9의 코드를 추가 시켜 줍니다.
                                                                                                                       <그림 1-9>

 
GetOutputCacheProviderName 메서드는 Web.config 파일에 <caching>섹션이 추가 되어 있어야만 호출 되는 메서드 입니다. 그리고 코드를 보면 특정 페이지 에만 "SessionCache"라는 Provider명을 리턴하게 되어 있는데 이 부분은 Web.config의 <provider> 하위 노드의 Name Attribute와 매핑되는 키값입니다. 즉, 특정 페이지에 접근 시 개발자가 원하는 CacheProvider에 접근하여 커스텀한 저장소에 저장하게 하는 기능의 Entry Point 라고 보셔도 무방합니다.

그리고 UserControl을 생성하여 간단한 컨트롤을 작성하여 보겠습니다. 그리고 UserControl 선언적 구문 페이지 상단에는 <%@ OutputCache Duration="60" VaryByParam="None" providerName="SessionCache" %>
을 기입하여 줍니다. Duration의 의미는 소멸되기 까지 대기 시간으로 60초이고 VaryByParam은 Post든 QuerString이든 파라미터가 넘어오는 건마다 페이지를 저장시킬지의 여부를 선택합니다. 그리고 providerName은 해당 UserControl에서 제공할 Provider를 선택하는 Attribute입니다다.
그리고, 한가지 재미 있는점은 여러개의 Provider를 구현 하고 Web.config에 등록 시킨 후 각각 UserControl에 다른 Provider를 등록 시키면 Global.asax에서 한가지 ProviderName을 리턴 하더라도 각각의 Provider 저장소에 저장이 된다는 것입니다.

이제 마지막으로 웹 프로젝트에 CustomOutputCacheProvider 프로젝트를 참조한다. 그림 1-10r과 같이 전체적인 Test 솔루션 구성이 끝이 났습니다.

                                                                                                   <그림 1-10>

Walkthrough : Step 4. Running Web Application and Result

이제 마지막 단계 Test Application을 구동하고 값을 확인하는 작업만 남겨두고 있습니다. F5를 눌러 빌드를 한 후 정상적으로 빌드 성공이 되면 Web browser가 화면에 나타날 것 입니다. 제가 값을 확인 하기 위해 몇개 중단점을 찍어 값을 확인 하는 것으로 이번 아티클을 마무리 할까 합니다.

맨 처음 브라우저가 화면에 나타나고 그림 1-11처럼 Global.asax의 GetOutputCacheProviderName메서드에 중단 점이 걸리는 것을 확인 할 수 있습니다.
                                                                                                                              <그림 1-11>
그 다음 F5를 다시한번 눌러 다음 중단점으로 이동하면  우리가 만들어 놓은 TestCustomOutputCacheProvider 클래스의 Set메서드를 호출하는 것을 확인 할 수 있습니다.

                                                                                                                              <그림 1-12>
Text Visualizer로 값을 확인한 결과 그림 1-13과 같이 렌더된 Html 태그값이 저장소에 저장되는 것을 확인 할 수 있습니다.

                                                                                                         <그림 1-13>
마지막으로 인위적으로 포스트 백을 일으켜 저장소에 저장된 값을 가지고 오는 과정을 확인 하도록 하겠습니다. 화면에 버튼 이벤트를 일으키면 서버 쪽 Global.asax의 GetOutputCacheProviderName메서드를 재 호출 하여 페이지에 할당된 ProviderName을 리턴받은 후 TestCustomOutputCacheProvider 클래스의 Get메서드를 호출하는 것을 확인 할 수 있습니다. Get메서드에서는 이미 저장소에 캐싱된 Html 값이 반환 되어 사용자에게 전달되는 것을 확인 할 수 있습니다.

<그림 1-14>

정말 오늘 길게도 썼습니다. 씁씁후후~ 어떤 메카니즘을 가지고 동작하는지 대충 이해하셨을 듯 합니다. 그럼 다음에도 ASP.NET 4.0에 다른 기능에 대하여 알아보도록 하겠습니다.

[VSIX] 2-1. How to start VSIX programming

VIsual Studio Extensibility 2009. 6. 23. 13:35 Posted by 알 수 없는 사용자

VSIX 시작하기

본격적으로 VSIX 프로그래밍에 대하여 알아 보도록 하겠습니다. 일단 VSIX의 개념 부터 짚고 넘어가는 것이 순리일듯 합니다. 일단 이미 이전에 엄준일씨에게서 포스팅 된 Visual Studio 2010 Extension Manager를 참고하는 편이 나을 것입니다.
VSIX는 뭐의 줄임말일까요? Visual Studio 2010에서 새롭게 선보인 VSIX는 언뜻 Visual Studio Extensibility와 관련 있는 듯 하나 VSIX는 인스톨러와 관련이 있으므로 Visual Studio Installer Extension의 의미일 듯 합니다.(이건 필자의 생각은 아닙니다. 머리나쁜 필자가 저런 의미심장한(?) 단어를 생각해 낼 일이 없기 때문에)

앞서서 1. What is different from before version에서 포스팅한 내용에 추가적으로 이전 버전과는 다른 Deployment Packaging을 지원하기 시작하였습니다. 이전에는 Packaging을 하기 위해서는 Setup 프로젝트를 생성하고 Regpkg.exe를 통해 Package 등록에 필요한 일련의 과정들을 설정하여 Deployment하는 복잡한 과정을 거쳐야 하였습니다.


하지만, 이제 상황은 달라졌습니다. VSIX가 나오므로 해서 패키징과 배포가 아주 아주 용이해졌을 뿐이나라 Extension Manager를 통하여 설치된 VSX 목록을 관리 할 수 있는 편리함도 함께 추가 되었습니다.

VSIX는 일종의 범용적인 Package로써 .zip의 OPC(Open Packaging Convention)가 적용된 형태라고 볼 수 있습니다. 그럼 심심한데 압축을 풀어 볼까요. 실제 압축이 풀리는지 막 궁금하지 않습니까? 필자만 궁금한가? 필자만 변태인가? 흐흐

<그림 1-1>

테스트로 프로젝트를 생성하고 빌드를 한 상태로 Debug 폴더로 들어가 보겠습니다. 그림 1-1과 같은 화면이 눈앞에 보일 것입니다. 뭐 별다른건 없어 보이는군요. Manifest파일과 .vsix 파일 말고는 우리가 자주 보던 어셈블리와 디버그 파일입니다. 그러나 .vsix의 압축을 풀어 보겠습니다.

<그림 1-2>
그림 1-2와 같이 .vsix 파일명과 같은 폴더가 생성된 것을 볼 수 있습니다. (아~ 그래픽 툴에 상당히(?) 익숙하고 디자인 감각이 있는 필자는 <~요기 라고 표현함으로써 직관성과 예술성을 감미하였습니다.)
그럼 해당 폴더로 들어가 보겠습니다.

 
<그림 1-3>

역시나 기대 했던것 처럼 .vsixmanifest파일과 해당 어셈블리가 위치되어 있습니다. 근데, [Content_Type].xml은 뭥미? 그래서 까봤습니다.(궁금한건 못참는 이 죽일놈에 개발자 습성 때문에)
<그림 1-4>

역시나 .vsix 내부의 파일 타입이 OPC API 요건 대로 아주 잘 정리되어 있는것 을 볼 수 있습니다.
그럼, .vsix 컨텐츠 목록을 정리해보겠습니다.
 [Content_Type].xml  컨텐츠의 알려진 파일에 대한 정보 수집 장소
 extension.vsixmanifest  Extension의 정보를 담고 있는 manifest정도로 이해
 하면 될듯?. 일종의 config파일과 비슷(?) 오바일까요?
 Assembly file  우리가 개발한 어셈블리 파일
 나머지 듣보잡  기타 아이콘, 이미지 기타 등등 파일들
<표 1-1>
 이상으로 .vsix를 까발리는 행위를 마치고 순수 VSIX를 개발하기 위한 일련의 과정들을 설명하기로 하겠습니다.

[VSIX] 1. What is different from before version?

VIsual Studio Extensibility 2009. 6. 23. 02:30 Posted by 알 수 없는 사용자
VSX ( Visual Studio Extensibility ) 란?
Visual Studio Extensibility (이하 VSX )는 예전 부터 우리가 쓰고 있는 Visual Studio와 통합하여 개발 생산성을 높이고자 기존에 제공 되던 IDE환경에서 보다 커스트마이징한 환경을 구축하는 것을 말합니다.
이미 Third-party 솔루션을 제공하는 회사는 널리고 널렸으며, 회사 자체 Framework을 구축하고 있는 곳은, 자체적으로 개발 생산성 향상을 위하여 VSX를 제공하고 있는 것을 흔히 볼수 있을 것입니다.


 
이미 Visual Studio 2005 와 Visual Studio 2008에서 그 기능을 강력하게 지원하고 있으며, 기본적으로 Visual Studio SDK를 설치 하지 않아도 제공되는 Add-In Templete은 쉽게(?) 원하는 Application을 작성할 수 있도록 도와 주고 있습니다.

주로 VSX 종류는 3가지로 나눌 수 있습니다.

1. Visual Studio Add-In
우리가 쉽게 할 수 있는 Add-In은 그 자체만으로 강력한 기능을 제공합니다. Visual Studio의 오브젝트 모델에 접근 할 수 있고, 사용자 인터페이스를 추가하고, 여러 옵션 툴바, 메뉴 등의 IDE 요소에 접근하여 컨트롤할 수 있게 제공하고 있습니다. 주로 Add-In으로 Package보다는 간단하지만 그 기능만은 간단하지 않은 기능들을 선보이고 있으므로 충분히 이것으로도 원하는 산출물을 내놓을 수 있다고 생각하고 있는 바이지만, 이 아티클을 쓰는 이유와는 좀 다르므로 일단 패스하고 추후, 필자의 블로그를 통해 간단하게 나마 소개해보고자 합니다.(필자 엄청난 귀차니즘으로 그날이 언제쯤 올런지.... 쯧쯧)

2. Visual Studio Macro
아~ 매크로 이름만들어도 귀에 팍팍 꽂히는 소리가 들리는군요? MS Office Excel의 기능을 화려하게 만들어 주는 이놈이 Visual Studio에도 있었습니다.(실제 쓰는 사람은 몇 보지 못한 1人) Package보다 쉽고 Add-In 보다 쉽다라고 자부하는 매크로는 주로 아주 반복적인 작업에 많이 쓰입니다. 하지만 우리가 원하는 커스트마이징과 퐌타스틱한 Application에는 조금 아쉬운듯한 새로운 기능 추가 조건에는 맞지 않습니다. 하지만, 오토메이션 업무에는 뛰어난 발군의 기능(?)을 제공하고 있으니 무시할 수는 없는 노릇입니다.

3. Visual Studio Package
드디어 나왔군요 Package. 총 3가지 중 Visual Studio의 활용성과 기능 추가성에 대해서는 Package만한 놈이없을 정도로 그 기능적인 확장성을 인정 받습니다. Package는 Unmanaged 즉 Native언어로 구현 가능하며, 또한 C#, VB와 같이 CLR에서 동작할 수 있는 Managed Language로도 구현이 가능하다니, 필자와 같은 C# 개발자에게는 좋은 IDE를 제공하여 주는 셈입니다. 

이렇게 VSX를 이루고 있는 아이들을 살펴 보았습니다. 그럼, 과연 Visual Studio 2010과 이전 버전 (이하 Visual Studio 2008 또는 2005)과는 무엇이 틀린지 살펴 보도록 하겠습니다.(필자 생전 태어나서 처음 아티클 이란 것을 써서 그런지 몰라도 벌써 퇴근의 압박과 고질적인 손가락 관절염이 도지고 있네요.)

What is different?

가중 눈에 띄게 달라진점을 꼽으라면 역시나 .NET Framework 4.0 부터 지원하는 MEF일 것입니다. (필자 MEF 땜에 VSIX 테스트 프로젝트 생성하고 안돌아가 똥줄 좀 탔습니다.)
MEF에 대해서 머리에 물음표가 그려지는 개발자 여러분께서는 엄준일씨(땡초)가 고생 고생해서 써놓은 아티클을 참고 하길 바랍니다. (친절한 필자는 링크도 남겨주는 군화. http://www.vsts2010.net/13)
MEF 즉 단위 컴포넌트 별로 쪼개어 개발될 뿐만 아니라, 의존성 또한 약화 되어버리니 이건 Visual Studio에서 그토록 기다리고 기다리던 기능이 아닐까 합니다. (아무래도 이전 버전에서 패키지를 관리하기에는 좀 어려움이 있기는 했습니다.)
그리고, MEF는 필자 개인적인 생각이고 UI측면에서는 WPF로 바뀐 Editor라고 할까나... .NET Framework 3.0 부터 지원하던 WPF가 드디어 IDE에 녹여지게 되었고, 추가적인 Editor 기능 역시 WPF로 구현이 가능하게 되어 개발하면서 움직이는 화면을 보는 재미가 쏠쏠하지 않을까 합니다.(필자 UX 개발자가 아니어서 저런 기능까지 구현할까라는 의구심이 들기도 합니다. 만들어 놓으면 그림판만 WPF지 윈폼 컨트롤 못지 않는 딱딱함을 지원할것이 100%입니다.) 그리고 Package프로젝트 생성 시 이전 버전과는 달리 Editor Templete이 분리 되어 있는 것을 볼 수 있을 것입니다. 아마도 MS는 이번 2010의 Editor에 사활을 거는듯 합니다. 왜냐하면 Editor의 Architecture를 눈에 띄게 변경시켜 놓았으니 말입니다. 이번 Framework 변경사항과도 일맥 상통하게 확장성이 화두인듯 합니다.


What is Next?
  
 
다음 번부터는 Tutorial 형식으로 하나하나 따라해 볼 수 있도록 구성해볼 생각입니다. 설치에서 부터 간단한 어플리케이션 작성 그리고 개인적으로는 Azure 도 붙여 보고자 합니다. 아주 재미 있을 것 같지 않습니까? 필자 엉덩이가 들썩 들썩하는 소리가 들려 오는 군요.
참고로 국내에서는 VSX 개발에 대한 리소스가 충분치 않는 것이 사실입니다. 필자의 검색능력이 부족하여서 인지도 모르겠으나, 많은 관심을 가지고 본다면, 정말 재미있는 영역이 될것입니다.

Welcome to Dynamic C#(2) - Wanna be a polyglot.

C# 2009. 5. 17. 16:25 Posted by 알 수 없는 사용자

- 자넨 왜 그렇게 언어에 집착하는고?

넵, 확실히 저는 언어에 쫌 집착하는 편이긴 합니다. 우리가 말하고 쓰는언어도 꽤나 집착하는 편입니다. 언어를 제대로 배우려고 노력하다보면, 더 재밌는 걸 많이 접할 수 있게 되고 그 언어뿐만 아니라 언어를 쓰는 사람들의 사고방식도 아주 조금씩 이해하게 되기 때문이죠. 뭔가 보고싶은게 있는데 그게 제가 모르는 언어로 되어 있어서 못보는건 조금 슬픈일인거 같습니다. 개인적으로 부족한 실력이지만, 미드&일드를 아주 재밌게 즐기고 있습니다.

프로그래밍 언어도 비슷한 의미에서 집착하게 되는게 아닐까 싶습니다. 프로그래밍 언어에는 그 언어를 만들고 지지하는 사람들의 사고방식도 같이 배울 수 있게 되고, 점점 재밌게 할수 있는게 늘어나기 때문이죠. 최근에 언어에 대해서 아주 부족한 의견이지만 글을 썼던 적이 있습니다. 관심있으신 분은 보시고 따쓰한 피드백 주시면 완전 감사하겠습니다. 

아무튼 그런의미에서 F#에 C#에 Axum까지 건드려보고 있는거지요. 줏대가 없다거나 바람기가 있다거나 뭐 그런건 아닙니다. 그럼 본론으로 들어가서, 지난번엔 동적C#에 대한 이야기를 조금 드렸었습니다. 하지만 정작 알고 싶은건 dynamic키워드란게 생겼고 대충 오리꽥꽥 어쩌구 저쩌구 하는건 알겠는데, 어디다 써먹는거란 말이더냐? 뭐 그런거겠죠. 그래서 쌩초보이지만, 최대한 그런관점에서 접근해보고자 합니다. 그 첫번째가 실제로 프로젝트를 하다가 하나 느낀게 있어서 그걸 적어보고자 합니다. 



- 시나리오
(제가 모르는 해결방법이 있을수도 있습니다. 그럴땐 따쓰한 피드백을!)

LINQ to SQL(이하 L2S)로 프로젝트를 진행중입니다만, 데이터가 추가되거나, 수정되거나 삭제될때 그 값들의 이전/이후 데이터를 포함해서 그 데이터의 고유번호(seq)를 같이 저장하는 뭐 그런 시나리오입니다. 그래서 L2S에서는 아래와 같은 방법을 제공합니다.

 

ChangeSet changeSet = db.GetChangeSet();

foreach (Customer c in changeSet.Inserts)
{
	.......
}

 

하지만, 위와 같은 코드는 한번의 SubmitChanges로 변경이 일어나는 대상 테이블이 하나라면 Generic파라미터로 처리할 수도 있겠지만(ChangeSet에서 리턴되는 객체의 타입이 object입니다), 주문과 상세주문같이 한번에 여러개의 테이블에 변경이 일어난다면, 그닥 친절하지 못한 시나리오가 되겠습니다. 그래서 그때 들었던 생각이 "아 이거 dynamic키워드를 이용해서 오리꽥꽥타이핑(duck typing)을 이용하면, 쫌 쉽게 될거 같은뎅..." 였습니다. 그래서 한번 살짝 구현해봤습니다.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace FirstDynamic
{
    class Customer
    {
        public int Seq{get; set;}
        public string Name {get; set;}        
    }

    class Company
    {
        public int Seq{get; set;}
        public string Name{get; set;}
    }

    class Project
    {
        public int Seq{get; set;}
        public string Name{get; set;}
    }

    class NoneSeqThing
    {
        public string Name{get; set;}
    }

    class Logger
    {
        public void WriteLog(dynamic entity)
        {
            try{
                Console.WriteLine(entity.Seq);
            }
            catch(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
            {
                Console.WriteLine("Exception : " + ex.Message);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {            
            Logger logger = new Logger();

            Customer customer = new Customer
            {
                Seq = 1,
                Name = "Boram"
            };

            Company company = new Company
            {
                Seq = 6,
                Name = "NanoSoft"
            };

            Project project = new Project
            {
                Seq = 108,
                Name = ".NET 99.9"
            };

            NoneSeqThing noneSeqThing = new NoneSeqThing
            {
                Name = "None Seq Thing"
            };

            object[] entities = new object[] { customer, company, project, noneSeqThing };

            foreach (object entity in entities)
            {
                logger.WriteLog(entity);
            }
        }
    }
}

 

위에서는 몇가지 객체를 선언해서 DTO를 흉내냈고, Seq가 있는 타입과 없는 타입이 있는 것을 볼 수 있습니다. 그리고 ChangeSet에서 반환되는 객체를 그냥 dynamic으로 Logger에게 넘겨주고 있습니다. 그리고 Seq멤버변수가 있으면 남기고 없으면 안남기는 식으로 다른 처리를 해주고 있습니다. 그리고 결과는 아래와 같습니다.



그렇습니다, 이런식으로 dynamic을 이용한다면 지난 포스트에서 Paul Graham아자씨가 이야기 했던 나이들고 엄한 이모 컴파일러와 좀더 편하게 대화할 수 있는 법이죠. 써놓고 보니깐 포스트가 별거 없네요. 기분 탓이 아니라 제 내공 탓이겠죠. 여담이지만, 제가 해결한 방식은 리플렉션으로 Seq프로퍼티를 검색해서 유/무를 판별하는 방식을 이용했습니다. 확실히 손이 좀더 많이 가는 방법이죠.

이렇게 여러가지 패러다임에 익숙해지고 잘 활용할 수 있게 되면, 점점 더 빠르고 경쾌한 리듬으로 코드를 작성하는 일도 가능하지 않을까 생각해봅니다. 이제 프로그래밍을 하시는 분들도 세계화에 발맞추는 속도에 걸맞게 프로그래밍언어에 대해서도  ployglot이 되어야 하지 않을까 생각합니다. 다만, 제 생각엔 그러한 노력은 프로그래머에게 족쇄를 채우는게 아니라 오히려 더 큰 자유를 주는 일이라고 생각합니다. 그럼, 또 개발하다가 dynamic의 헬프가 필요한 순간이 오면 나누도록 하겠습니다.

여담이지만, polyglot하니깐 중학교때 "핸들 이빠이 돌려"라는 한문장으로 3개국어를 자유자재로 구사하시던 한문선생님이 떠오르는 군요. ㅋ


-참고자료
1. Pro LINQ: Language Integrated Query in C# 2008, Joseph C. Rattz, Jr. , APRESS.

Welcome to F#(8) - 은총알과 엄친아.

F# 2009. 5. 10. 20:56 Posted by 알 수 없는 사용자
-넌 제목이 왜 맨날 이 모냥이냐.

은총알과 엄친아. 과연 전 무슨생각으로 제목을 맨날 이렇게 짓는 걸까요? 개인적으로 포스트에서 이야기 하고자 하는걸 비유적으로 설명하는걸 좋아합니다. 그래서 일까요? 늘 글을 쓰기 전에 이야기 하고자 하는 내용을 심사숙고합니다. 그렇게 해서 나오는게 고작 요정도 인거죠.

은총알. 은초딩도 아니고 이건 뭘까요? 들어보신분들도 많으시겠지만, 소프트웨어 공학에서 이야기하는 모든 문제가 이거하나면 해결된다고 하는 그런 어떤 경우에도 쓸 수 있는 만능의 해결책? 그정도 느낌을 생각하시면 될거 같습니다. 즉, 많은 사람들을 파닥파닥 낚이게 만드는 주범이지요. 엄친아도 못하는게 없는 만능의 엄마친구아들을 뜻하죠. 적어도 프로그래밍 세계에선 은총알이나 엄친아 같은 프로그래밍언어는 없는거 같습니다. 아무리 C#이 발전했다 한들, 하자면 할 수 있지만 어색한 부분이 있다는 거죠. 그럼 오늘은 거기에 대해서 좀 이야기 해보겠습니다.


- 이거슨 F# 코드!

아래 코드는 Expert F#에 나오는 코드입니다.

open System.Collections.Generic

let devideIntoEquivalenceClasses keyf seq =
    let dict = new Dictionary<'key, ResizeArray<'a>>()
    
    seq |> Seq.iter(fun v ->
        let key = keyf v
        let ok, prev = dict.TryGetValue(key)
        if ok then prev.Add(v)
        else 
            let prev = new ResizeArray<'a>()
            dict.[key] <- prev
            prev.Add(v))
            
    dict |> Seq.map(fun group -> group.Key, Seq.readonly group.Value)

즉, 어떤 데이터의 집합과 그 데이터에 대해 수행할 함수를 받아서 각각의 데이터에 대해 그 함수를 수행합니다. 그리고 같은 결과 끼리 집합을 만들어서, 결과값과 결과값이 같은 데이터들의 집합을 Dictionary로 만들어서 리턴해주는 프로그램이죠. 즉, 함수형언어의 장점이 잘 드러나는 데이터처리부분의 예제입니다. 실행결과는 아래와 같습니다. 

> devideIntoEquivalenceClasses (fun x -> x % 3) [1..10];;
val it : seq<int * seq<int>>
= seq [(1, seq [1; 4; 7; 10]); (2, seq [2; 5; 8]); (0, seq [3; 6; 9])]

숫자를 3으로 나눈나머지를 구하는 함수와, 1부터 10까지의 숫자를 넘겨주면, 이렇게 나머지값이 같은 숫자별로 그룹을 지어서 리턴해줍니다. 여기서 Dictionary는 닷넷의 클래스를 그대로 사용한 거구요, ResizeArray는 닷넷에서의 System.Collections.Generic.List<T>의 줄임말입입니다. 이름그대로 mutable한, 사이즈를 조절가능한 Array라는 거죠. 

코드를 보면, 전체데이터를 포함할 dict라는 Dictionary를 선언하구요, 입력으로 들어온 seq의 각요소에 대해서 사용자가 넘겨준 함수(keyf)를 수행합니다. 그리고 그 결과값이 이미 존재하는지 확인해서 존재한다면, 그 결과값을 key로 하는 ResizeArray에 숫자값을 추가하고 존재하지 않는다면, 새로운 ResizeArray를 만들어서 숫자를 추가하고, 그 ResizeArray를 dict에 추가해주는 방식입니다.

그리곤 마지막으로 key에 해당하는 ResizeArray를 읽기전용인 시퀀스로 만들어서 리턴해주고 있습니다. 이 예제는 어떤 함수에서 내부적으로는 side-effect를 가진 자료구조를 쓰더라도 그 side-effect를 오직 내부에서만 나타나게 격리한다면, 그 함수는 pure하고 functional한 함수라는 이야기를 하면서 나온 예제입니다. 그래서 마지막에 읽기전용 시퀀스로 리턴을 해주고 있는거지요. 


- 이거슨 C#코드!

이 코드를 C# 3.0버전으로 그대로 옮겨 보겠습니다. 

class Program
    {
        public Dictionary<T2, List<T1>> DivideIntoEquivalenceClasses<T1, T2>>(Func<T1,T2> func, List<T1> list)
        {
            Dictionary<T2, List<T1>> dict = new Dictionary<T2, List<T1>>();

            list.ForEach(l => {
                T2 key = func(l);
                if (dict.ContainsKey(key))
                {
                    dict[key].Add(l);
                }
                else
                {
                    List<T1> prev = new List<T1>();
                    dict[key] = prev;
                    prev.Add(l);
                }
            });

            return dict;
        }

        static void Main(string[] args)
        {
            Program program = new Program();

            var dict = program.DivideIntoEquivalenceClasses((x => x % 3), new List<INT> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });

            var resultSet = dict.Select(item => new { item.Key, item.Value });

            foreach (var result in resultSet)
            {                
                Console.Write("{0},", result.Key);
                result.Value.ForEach(item => Console.Write(" {0} ", item));
                Console.WriteLine();
            }
        }
    }

실행결과는 아래과 같구요.



역시 func<>를 이용한 higher-order와 LINQ덕분에 코드의 길이자체가 별로 차이가 없고, 오히려 C#의 문법에 익숙해서 그런지 더 깔끔해 보이는 부분도 있네요. 다만, 눈에 상당히 거슬리는게 있다면, T1,T2를 비롯한 파라미터타입과 리턴타입등을 명시한 부분이 상당히 거슬리는군요. 사실 짜면서도 아직 익숙하지 않아서 일까 좀 헷갈렸습니다. F#에서는 Type inference가 제대로 적용되어서 파라미터에도 타입을 명시할 필요가 없죠. 물론 C#에서도 변수수준에서는 var타입을 이용해서 Type inference를 지원하지만, 파라미터와 리턴타입에 대해서는 지원하지 않기 때문에 쫌 복잡해질 수 있습니다. 함수형 프로그래밍을 C#에서 하느라 복잡해지는 모냥은 여기에서도 확인하실 수 있습니다. @_@.

 
아래구절은 Expert F#에서 인용한 구절인데요, 제가 설명하려는 바를 아주 잘 요약해주시고 있습니다.

 A key feature of F# is the automatic generalization of code. The combination of automatic
generalization and type inference makes many programs simpler, more succinct, and more

general. It also greatly enhances code reuse. Languages without automatic generalization force programmers to compute and explicitly write down the most general type of their functions, and often this is so tedious that programmers do not take the time to abstract common patterns of data manipulation and control.

F#의 핵심기능중의 하나가 바로 코드를 자동으로 일반화시켜주는 것이다. 자동일반화와 타입유추를 조합하게 되면 많은 프로그램을 간단하고, 좀더 명확하게 그리고 좀 더 일반화시켜서 작성할 수 있다. 당연히 코드 재사용도 아주 편리해 진다. 자동일반화를 지원하지 않는 언어는 프로그래머가 명시적으로 일반적인 타입을 계산해서 명시해야 만 한다. 그리고 이런 것들은 데이터처리와 그 처리의 흐름을 공통적인 부분을 묶어서 추상화하는 작업을 매우 손이 많이가고 짜증나는 작업으로 만든다.



- 그래서~?

즉, LINQ를 통해 C#에서의 데이터처리와 흐름의 추상화가 가능해졌으며, 그 덕에 C#으로도 부분적으로 함수형언어와 버금가는 생산성을 내는일이 가능해졌습니다. 하지만, 명령형 언어의 한계점이 이런부분일까요? 일반화를 하기 위해서 무척이나 까다롭게 인자들의 타입을 잘 명시해줘야 하는거죠. 물론, 저렇게 하는게 옳은지에 대해서는 잘 모르겠습니다. 개인적으로는 저런부분을 굳이 C#을 이용해서 구현하려고 애쓰기 보다는 F#을 이용해서 조합하는게 더 옳지 않나 싶습니다. 사실 F#을 닷넷에 편입시키려는 의도도 그런부분을 가장 기대하는게 아닌가 싶기도 하구요. 


- 참고자료

1. Expert F#, Don Syme, Adam Granicz, Antonio Cisternino, APRESS
2. http://weblogs.asp.net/podwysocki/archive/2009/04/26/functional-c-forward-functional-composition.aspx

 

Welcome to Dynamic C#(1) - 첫만남.

C# 2009. 5. 4. 14:37 Posted by 알 수 없는 사용자

-너 F#쓰던 넘이자나, Dynamic C#은 뭐냐.

안녕하세요. Welcome to F#이라는 앞뒤도 안맞고 내용도 부실하며, 불친절한 포스트를 남발하고 있는 강보람(워너비)입니다. 원래 하던거에 약간 시들해지면, 새로운 자극을 찾는다고 하던가요. F#의 포스트를 쓰는게 점점 벽에 부딛히니 C#과 자극적인 외도를..... 생각하는건 아닙니다. 하지만, 의문이 생기니 그걸 찾아보고자 하는 호기심이 생기고, 그 호기심을 오래잠재워두면 없어질거 같아서 일단 시리즈를 시작해보자! 해서 일단 무작정 Welcome to Dynamic C#이라는 시리즈를 시작해볼까 합니다. 기존에 동적언어에 대한 경험이 일천하다보니 이 시리즈의 내용역시 상당히 불친절할걸로 예상되지만, 관심있으신 분들께선 따쓰한 피드백을 주시기 바랍니다. 이제 날씨가 더워지는데 따쓰한 피드백을 주면 제가 열사병으로 죽지는 않을까 하는 염려는 고이접어 간직하시고 따쓰한 피드백을....-_-


-Paul Graham아저씨와 실용주의 아저씨들?

Paul Graham을 아십니까? "Hackers and Painters"의 저자이며, 논쟁을 불러일으킬만한 말을 참 많이도 하는 Lisp빠돌이죠. 오랜만에 Hackers and Painter를 읽다가 아래와 같은 구절을 읽고는 '이 책에 이런구절이 있었나~?' 하면서 C# 4.0의 dynamic과 DLR이 떠올랐습니다. 

A programming language is for thinking of programs, not for expressing programs you've already thought of. It should be a pencil, not a pen. Static typing would be a fine idea if people actually did write programs the way they taught me to in college. But that's not how any of the hackers I know write programs. We need a language that lets us scribble and smudge and smear, not a language where you have to sit with a teacup of types balanced on your knee and make polite conversation with a strict old aunt of a complier. 

프로그래밍언어는 프로그램에 대해서 생각해나가기 위한 것이지, 이미 머리속에 짜여져있는 프로그램을 표현하기 위한 것이 아닙니다. 프로그래밍언어는 펜이 아니라 연필이어야 합니다. 정적타입은 제가 학교에서 배우던 것 처럼만 프로그램을 짠다면 괜찮은 아이이디어 일지도 모르겠습니다. 하지만 제가 아는 해커들중에 그렇게 짜는 사람은 한명도 없더군요. 우리는 명확하지 않은 아이디어를 흐릿한 형태 그대로 빠르게 묘사할 수 있게 해주는 언어가 필요합니다. 무릎에 타입이라는 찻잔을 떨어지지 않게 올려놓고, 나이많고 엄격하신 이모님이랑 공손하게 대화하려고 애쓰는 것 처럼 컴파일러와 대화할 필요가 없다는 거죠.


그리고는 예전에 사두었던 "Programming Ruby"를 꺼내서 읽어봤습니다. "실용주의 프로그래머"를 읽으면서 실용주의 학파로 유명한 데이비드 토머스와 앤디 헌트가  펄에서 루비로 관심사를 옮겼다는 사실은 알고 있었지만, 이 책을 읽으면서 이들의 동적언어에 대한 사랑이 확실하게 드러나더군요.

반면에 루비를 잠깐이라도 사용해본다면, 동적 타입을 갖는 변수가 많은 점에서 실질적으로 생산성을 향상시킴을 알게 될 것이다. 오랜 기간 실행되는 커다란 루비 프로그램이 중요한 작업을 수행하면서도, 타입과 관련된 오류는 하나도 던지지 않고 잘 돌아간다. 왜 일까? ..... 결론적으로 말해 '타입 안전성'에서 말하는 '안전성'이란 대개 허상에 불과하며, 루비 같은 동적 언어로 개발하는 것은 안전하면서도 생산적이다.

이런 호기심이 생기자, F#때문에 미뤄오던 C#4.0의 dynamic에 대한 탐구를 더 이상 미룰 수 없다는 생각이 들었습니다. 그래서 가끔 생각날때 마다 dynamic에 대한 생각을 정리해서 올리려고 지금 포석을 깔고자 하는 것이죠. 대뜸 F#에 대해서 쓰다가 C#으로 옮겨가면, 읽으시는 분들도 혼란을 느끼시지 않을까 해서 말이죠. 물론 그런분덜 한분도 없을거 압니다-_-. 


-반갑다! dynamic! 

일단, dynamic에 대해서 조금 알아보도록 하겠습니다. dynamic은 말 그대로 동적이라는 거구요, 타입체크를 컴파일 타임이 아니라 수행순간에 즉, 런타임에서 하겠다는 말이 됩니다. 여기서 Duck typing이라는 개념이 들어가게 되는데요, Duck typing은 객체의 타입에 따라서 가능여부를 결정하는 게 아니라, 그저 요건을 갖추고 있다면 모두다 가능하다고 보는 거죠. 즉, 비유를 하나 하자면 조선시대의 어떤 모임을 생각해 보겠습니다.

이 모임에서 양반집의 지체높은 어르신들과 그 어르신들의 자제들만 회원으로 받아준다면 이 모임은 정적타입검사(Static type check)를 하고 있는셈입니다. 특정 계급(Type)만 들여보내주기 때문이죠. 타입이 틀리면 아예들어갈 수가 없습니다. 하지만, 어떤 모임에서는 그림에 관심이 있는 사람이라면, 양반이나 상놈 가리지 않고 모두다 받아들여줬습니다. 이 모임은 바로 Duck typing을 충실히 따르고 있는셈입니다. 계급(타입)에 상관없이 그저 공통점(그림을 사랑하는 뜨거운 가슴!)만 있으면 들여보내주기 때문이죠. Duck typing은 이처럼 타입으로 가르지 않고, 요건을 갖췄다면 모두 묻지도 따지지도 않고 받아들여주는 걸 이야기 합니다.(Duck이 들어가 있는 이유는 애초에 이 개념을 설명하신 분께서 오리처럼 걷고 꽥거리면 전부다 오리로 봐주자고 말씀하셨기 때문이죠. 제가 안그랬습니다-_-. 오히려 제가 만들었다면, '동호회 타이핑' 같은용어를 썼을거 같은데... 제가 안만든게 다행이군요.) 

그럼 첫 예제를 한번 볼까요? 

static void Main(string[] args)
{
            dynamic num = 4;

            Console.WriteLine(num);
 

            Type type = num.GetType();
            MethodInfo method = type.GetMethod("ToString", new Type[] {});

            if (method != null)
            {
                Console.WriteLine(method.ToString());
            }
} 

위 예제는 dynamic타입의 num에 숫자인 4를 입력하고, 출력을 합니다. 그리고 WriteLine에 의해서 불려지는 ToString메서드의 유무를 검사하기 위해서, 타입에서 ToString메서드를 찾아서 출력하는 프로그램입니다. 결과는 아래와 같습니다. 



WriteLine에선 출력할 객체의 ToString을 호출하죠. 그래서 num의 값이 출력되는 것을 보면 런타임에 num이 ToString을 가지고 있는지 확인해서 있으니까 출력을 했다고 볼 수 있습니다. 그리고 확인을 위해서 dynamic타입인 num의 타입에서 ToString메서드를 찾아서 해당 메서드를 출력해봅니다. 결과를 보면 ToString이 있는 것을 볼 수 있습니다. 그리고 WriteLine의 오버로딩중에서 dynamic타입을 인자로 받아들이는 오버로딩이 없는걸로 봐서는, 런타임에 해당 dynamic객체가 ToString을 가지고 있는지 봐서 있으면 출력하고, 없으면 Microsoft.CSharp.RuntimeBinder.RuntimeBinderException을 발생시키는 것을 유추할 수 있습니다.


-양반과 상놈의 이야기 

그럼 위에서 이야기 했던, 양반과 상놈의 이야기를 한번 구현해볼까요? 

class Yangban
    {
        public string Name {get; set;}

        public void YangbanSound()
        {
            Console.WriteLine("Yo, it's the yangban sound, baby :) ");
        }
    }

    class YangbanChild : Yangban
    {
        public void ILikeDrawing()
        {
            Console.WriteLine("I like Drawing");
        }
    }

    class Sangnom
    {
        public string Name { get; set; }

        public void SangnomSound()
        {
            Console.WriteLine("Yo, it's the sangnom sound, baby ;) ");
        }

        public void ILikeDrawing()
        {
            Console.WriteLine("I like Drawing");
        }
    }

    class Program
    {
        public void EnteringYangbanClub(Yangban yangban)
        {
            Console.WriteLine("Welcome yangban {0}!", yangban.Name);
        }

        public void EnteringDrawingClub(dynamic person)
        {
            try
            {
                person.ILikeDrawing();
                Console.WriteLine("Welcome {0}! who like drawing",person.Name);
            }
            catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException you_do_not_like_drawing)
            {
                Console.WriteLine("{0}. You can't come in. I'm sorry about that :)", person.Name);
            }
        }

        static void Main(string[] args)
        {
            Yangban yangban1 = new Yangban { Name = "chulsoo" };
            YangbanChild yangbanChild1 = new YangbanChild { Name = "chulsooAdeul" };
            Sangnom sangnom1 = new Sangnom { Name = "boram" };

            Program program = new Program();
            program.EnteringDrawingClub(yangban1);
            program.EnteringDrawingClub(yangbanChild1);
            program.EnteringDrawingClub(sangnom1);
        }
    }

위 코드를 보시면, 양반과 양반자식, 상놈이 있죠? 모두 이름을 가지고 있구요, 양반은 간지나는 양반사운드를 구사하며, 상놈은 상놈사운드 그리고 양반자식과 상놈은 자기가 그림그리는걸 좋아한다고 합니다. 그리고 두개의 클럽이 있습니다. 하나는 양반만 들어갈 수 있는 클럽이구요, 하나는 그림을 좋아하는 사람이 들어갈 수 있는 클럽입니다. 자 그럼, 양반과 양반자식인 철수와 철수아들, 상놈인 보람을 데리고 이야기를 해보겠습니다. 양반클럽에 철수와 철수아들, 보람 이렇게 셋이 들어가려고 합니다. 그런데, 양반클럽은 양반이거나 양반의 자제가 아니면 못들가게 엄격하게 막아놨습니다. 그래서 일까요? 아래 그림을 보면, 들어가고 싶다는 의사표시조차도 허용되지 않는 슬픈 모습입니다. 

뭐, 계급으로 딱 막혀있기 때문에 들어가는거 자체가 불가능하니까요. 이렇게 타입으로 조건을 걸게되면, 해당타입이거나 해당타입을 상속한 타입이 아니면 안됩니다. 그러면, 그림애호가 클럽에 들어가보도록 하겠습니다. 그림애호가클럽은 일단 다 들여보내주는 군요. 하지만, 들어가게되면 그림을 좋아하는지 크게 외쳐보라고 시킵니다. 근데, 철수는 그림을 좋아하지 않는군요. 그래서 철수는 쫓겨나고 철수아들과 보람은 환영받습니다. 실행결과는 아래와 같죠.


그렇습니다. 계급으로 나누것이 아니라 무엇을 할 수 있는지에 따라 니편 내편을 나누니깐 철수는 못들어가고 보람은 들어가게 되는 것이죠.


-근데 dynamic이거 어따 쓰면 조으까? 

도대체 이걸로 무엇을 할 수 있다는 걸까요? 왜 Paul Graham아저씨는 그토록 다이나믹을 부르짖었으며, 실용주의 아저씨들은 또 왜그랬을까요? 불행히도 저는 그동안 정적인 명령형 언어만 다뤄봤기 때문에 잘 모르겠습니다. 뭐 좀 알아보려고 하는데, 그 과정에서 느끼는 점들을 이곳을 통해 나눠보려고 하는거구요. 그럼 F#뿐만 아니라 C#을 통해서 최대한 자주 찾아뵙겠습니다.


-참고자료

1. Hackers and Painters, Paul Graham, O'Reilly
2. Programming Ruby, 데이비드 토머스, 앤디 헌트, 차드파울러, 인사이트
3. http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=csharpfuture&DownloadId=3550