Welcome to Parallel C#(2) - 계속 되는 개념 찾기.

C# Parallel Programming 2010. 5. 27. 09:00 Posted by 알 수 없는 사용자

- 개념!!

군 생활을 하다 보면, 개념 없는 사람들을 많이 보게 됩니다. 가끔은 '내가 평생 이런 놈을 다시 볼 수 있을까?'싶은 사람도 보게되죠. 그리고 인간관계란 과연 무엇인지 원점에서 다시 생각해 보게 됩니다. 개념 없는 사람들도 사회에서 담당하는 역할이 있는 셈이죠. 오늘은 병렬 프로그래밍에 필요한 개념들을 같이 채워볼까 합니다. 개념 충만한 사람들은 훠이훠이~ 절로 가시고(저는 종교의 다양성을 존중합니당. 교회로 가실 분은 교회로..), 개념 없는 사람들만 저랑 같이 개념을 채워 보시져. 훗.


- 기본 개념!!!

스레드(Thread)

스레드는 다른 순차적 명령 집합(sequence of instructions)과 동시적으로 실행 가능한 순차적 명령 집합을 이야기 합니다. 예를 들면, 뭔가 작업을 처리하는 스레드가 있고, 그 작업의 현황을 화면에 표시하는 스레드가 있는 것 처럼 말이죠. 두 스레드는 서로 다른 순차적 명령 집합을 가지며, 동시적으로 실행 가능하죠. 그리고 두 개 이상의 스레드를 동시적으로 실행하는 것을 멀티스레딩(multithreading)이라고 합니다. 흔히 이야기 하는 동시성이나 병렬성도 최대한 효율적으로 스레드를 여러개 사용하는 것이므로, 멀티스레드의 범주에 들어간다고 할 수 있겠습니다. 운영체제는 이런 멀티 스레드를 동시적으로 처리하는 것 처럼 보여주기 위해서 시간 쪼개기(time slicing)이라는 기법을 활용합니다. 즉, 사람이 눈치채지 못할 정도로 빠른 시간만큼 한번에 하나씩 스레드를 처리하는 것이죠. 단순하게 생각해서, 음악을 들으면서 비주얼 스튜디오로 코딩을 한다면, 사람이 눈치채지 못할 정도의 속도로, 예를 들면 1/24초 정도의 속도로 한번은 음악재생, 한번은 비주얼 스튜디오, 또 한번은 음악 재생, 또 한번은 비주얼 스튜디오 이런식으로 번갈아 가면서 처리하는 것이죠. 이걸 문맥 교환(context switching)이라고 합니다. <그림1>을 보시죠.

<그림1>본격 OS 스케쥴링 그림.jpg

<그림1>에서 나온 것 처럼, 사용자가 눈치채지 못 할만큼의 속도로 두 작업을 번갈아 가면서 CPU가 처리하도록 OS가 스케쥴을 조절하는 것이죠. 그런데 만약, 스레드가 너무 많아서 제대로 CPU를 점거하지도 못하고, 작업을 계속해서 교체하게 되면 어떻게 될까요? 실제로 CPU가 작업을 처리하는 시간보다, 문맥 교환에 더 많은 시간이 들어가게 되므로, 음악이 벅벅 끊기는 등의 지름유발상황이 생기겠죠. 그럼, 시간 쪼개기를 한번 시뮬레이트 해 볼까영? 어헣.

using System;
using System.Threading.Tasks;

namespace Exam2
{
    class Program
    {
        static void Main(string[] args)
        {
            const int max = 10000;

            //현재 작업중인 스레드외에 추가로 스레드를 생성
            Task task = new Task(() =>
                {
                    for (int count = 0; count < max; count++)
                    {
                        Console.Write("|");
                    }
                });

            //추가 스레드 시작
            task.Start();

            //현재 작업중인 스레드에서도 반복문 시작
            for (int count = 0; count < max; count++)
            {
                Console.Write("-");
            }

            //혹시 현재 스레드가 빨리 끝나더라도,
            //추가 스레드가 끝날 때 까지 기다리기.           
            task.Wait();
        }
    }
}

<코드1>아주 단순한 코드

<코드1>은 아주 단순한 코드입니다. 콘솔 어플리케이션의 실행을 담당하는 스레드 외에, 추가로 스레드를 하나 더 생성해서, 두 스레드에서 똑같은 반복문을 돌면서 서로 다른 문자를 출력하게 하는 거죠. 결과를 볼까요?

-|---||||||||||||||||------|||||||||||||||---------------|||||||||||||-----------||||||--------------|||||||||||||--------------|||||||||||||------------||||||||--------------||||||||||||----------||||||||||||||---------|||||||||||||--------|||||||||||||||--------------|||||||||||||||-------------||||||||||||-||||||||||-----------------|||||||||||||||-------------||||||||||||||--|||||||||||||-------|||||||---------------|||||||||||||||----------||||||||||||||---------|||||||||-----------------||||||||||||||-------------|||||||||||||-------------||||||||||-----------------|||||||||||||------------|||||||||||||-----------||||||||------|||||||||||||--------|||||||||||||||-----||||||||||||||-------------||||||||||||----------------|||||||||||||---------------|||||--------------|||||||||||||----||||||||||||||||--------------|||||||||||||||---------||||||||||||-----------|||---------------||||||||||||||||----------------||||||-------------|||||||||----|||||||||||||||||-------------||||||||||||||-||||||||||||||-------------||||||||-------|||||||||||||||--------|--|||---------|||||||||||||--------|||||||||||---|||||||||||||||----------------|||||||||||||||--------------||||-----------------------------------------------------------------------------------------------
계속하려면 아무 키나 누르십시오 . . .

<결과1>평범한 결과

서로 다른 문자가 출력되는 곳이 바로, 시간 쪼개기로 인한 문맥 교환이 일어나는 곳입니다. 정해진 시간만큼 한 스레드가 CPU를 점거하고 작업을 하는 것이죠. 그러면, 약간 방해를 해볼까요? 코드를 다음과 같이 약간 수정해봅니다.

Console.ReadLine();

//추가 스레드 시작
task.Start();

<코드2>수정된 아주 평범한 코드

그리고 이 프로그램이 사용자의 입력을 기다리는 동안, 다음과 같이 프로세스의 우선순위를 낮춰버립니다.

<그림2>프로세스 우선순위 낮추기 ㅋ

그리고 한번 실행해 볼까요? 프로세스의 우선순위가 낮아지면, 실행중인 작업이 우선순위가 높은 다른 프로세스에게 방해를 받을 가능성이 높아지는 거죠.

---------|||||||----||||||||||||||||-----||||||||||||--------||||||||||||||------------|||||||||----------------||||||||||||||------------||||||||||||||-------------|||||||||||----------------|||||||||||||-----------||||||||||||||-------------||||||||||||--------||||||||||||||------------|||||||||||||||-------------|||||||||||||------||||||||||||||-------------||||||||||||||--------------|||||||||||-----------||||---------------|||||||||||||-------------||||||||||||||-------|||||||||||||----|||||||||||||--------------|||||||||||||||-------------|||||||||||||-|||||||||||---------------||||-------------|||||||||||||-------------|||||||||||||---------|----------------|||||||||||||-------------||||||||||||||-----------||||||||||||---------------|||||---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
계속하려면 아무 키나 누르십시오 . . .

<결과2>낮은 우선순위 결과

그런데, <결과2>를 보니, 얼마나 방해를 받은 건지 솔직히 잘 모르겠네요.('-'가 마지막에 막 출력된 건, '|'를 출력하는 스레드가 빨리 작업을 끝내서 입니당) 제 컴이 쿼드에 2.7GHz짜리인데, 아마도 이정도로는 방해가 안되는 거 같습니다. 그래서! 비주얼 스튜디오 2010 20개랑, 음악을 틀고, 바이러스 검사를 돌리고, 인터넷 익스플로러 8도 창을 7개정도 띄워놓고 실험을 해봤습니다.

-||||||||||||||||-------------|---------------|--------------||||||||||----------||||||||||||||-----------||||||||||||--------------|||||||||||---------||||||||-|||||||----|||||||||------------|||||||||||||-------------|||-----------||||||||---------------||||||||||||||-------------||||||-------------||||||||||--------||||||||||||||----|||||||||||||||--------------||||||||||||-----------||||||||||----------------|||||||||||----------|------------||-|||||||||------||-------|||--------------|||||||||||||------------|||||||||||||-------------|||||||||||----|||||||||||-----||||||||||||||---------------||||||||||||||------------|||||||||----------------||||||||||||--------|||||||||||||-------------|||||||||-------|||------------|||||-----|||||||------||||||||||||||||---------------------||||||------|||||||||||||||-------------||||||||||||||-------------||||||||||||--------||||||||||||||||-------------||||||||||--------------|||||||||-------||||------|----||||||-----||||||||||||||-------------|||||||||------------|||||||||||-------||||||||||||||--------------||||||||||||||--------------||||||||||--------||||
|--------------||||||||||||||||-----------|||||||||||||--|||||||||||------||||||||----------------||----||||||||||||||-----||||||||||||||||||||||||||||||||||||||
계속하려면 아무 키나 누르십시오 . . .

<결과3>갖가지 태클을 가한 결과

<결과3>을 보시면, 어느 정도 패턴이 불안정 해진 걸 보실 수 있습니다. 제대로 처리도 못하고, '|'이거 딱 한개 출력하고 문맥 교환이 일어난 부분도 꽤 보이네요.


원자성(Atomicity)

원자성은 뭘까요? 원자성을 만족하는 조건을 보지요. 아래 조건 중에서 하나를 만족하면 원자성을 만족하는 것입니다.

1. 작업을 처리하기 위한 명령어 집합은 다른 작업이 끼어들기 전에 반드시 완료되어야 한다.
2. 작업을 처리 중인 시스템의 상태는, 작업을 처리하기 전의 시스템의 상태로 돌아갈 수 있어야 한다. 즉, 아무 작업도 처리되지 않은 시스템의 상태로 돌아갈 수 있어야 한다.

예를 들면, 은행 계좌에서 돈을 송금하는 걸 생각해보면요. 돈을 송금하기위한 몇가지 단계가 있습니다. 일단 계좌에 돈이 있는지 확인하고, 보낼 계좌가 존재하는지 확인하고, 그리고 돈을 실제로 송금하는 것이죠. 이렇게 송금을 위한 각 단계를 밟는 와중에, 돈을 인출한다던가 하는 작업이 끼어들면 안되겠죠. 그리고 송금 절차가 완료되기 전에는 계좌에 아무런 변화가 없어야 하는 거죠.

C#에서 num++같은 연산을 보면, num의 값을 가져오고, num의 값을 1 증가시키고, num에 증가된 값을 넣는 절차를 밟는데요. ++연산자는 원자성을 만족하지 못합니다. 그래서 num에 증가된 새 값을 넣기 전에, 다른 스레드가 원래 값을 변경 시켜버린다던가 하는 일이 벌어질 수 있죠.


데드락(Deadlock)

데드락은 죽음을 잠근다는 뜻이므로, 죽지 않는다는 뜻입니다. 네, 저를 죽이려고 달려드는 사람들의 모습이 보이네요. 모두들 잠깐만 진정하시고....-_- 이건 서로 볼을 꼬집고서, '니가 먼저 안놓으면, 나도 안놓을 꺼임'하고 외치고 있는 꼬마들을 생각하시면 됩니다. 꼬맹이들은 괜한데서 자존심을 세우죠. 그래서 먼저 놓으면 지는거라고 생각해서 안 놓습니다. 그래서 서로 계속해서 볼을 꼬집고 질질 짜게 되는 거죠. ㅋㅋㅋ

한 스레드는 A라는 자원을 점거하고, B를 가지고 와야 A를 놓아줍니다. 그리고 또 다른 스레드는 B라는 자원을 점거하고, A를 가져와야 B를 놓아준다고 해보죠. 그러면 이 스레드들은 서로 상대방이 가진 자원을 하염없이 기다리는 상황이 됩니다. 죽을 때까지 그 상태로 기다리겠죠. 그래서 이건 데드락입니다. 어헣.


- 정리하면서

위에서 설명드린 원자성이나 데드락은 여러개의 스레드에서 어떤 순서로 명령어를 실행하느냐에 따라서 복잡하게 발생합니당. 이런 프로그램을 짜려면 머리 빠지겠죠. 아이 무셔워라. 그래서 조심해야 하는 것이고, 이런 실수를 최대한 줄여주는 도구가 있어야 하는 것이죠!!

이게 왠 운영체제 수업이냐 하고 반감을 가지시는 분덜도 있으시리라 생각이 됩니다. 모.. 복습했다 생각하시고 어헣. 좋은게 좋은거니깐.. 어헣.


- 참고자료

1. http://www.cafeaulait.org/course/week11/03.html
2. Essential C# 4.0, Mark Michaelis, Addison Wesley

Welcome to Parallel C#(1) - 굿바이, 그리고 안녕~~?

C# Parallel Programming 2010. 5. 24. 09:00 Posted by 알 수 없는 사용자

- 끝은 또 다른 시작일 뿐.

인생에서는 항상 뭔가가 끝나면 뭔가가 시작되기 마련입니다. 직장을 그만두면, 직장인은 끝나지만, 백수가 시작되죠. 직장을 구하면, 백수가 끝나고 직장인을 시작하는 것이구요. 길고 길었으며, 별로 인기 없었던 Welcome to dynamic C#이 끝나고, 앞으로도 얼마나 길고 길지 모르며, 인기도 없을 Welcome to Parallel C#이 시작됩니다. 인생에선 끝이 안나는 것도 있더군요. 허접함은 불치병이라고 들었습니다. 시한부면 좋으련만. 흥. 그래도 이 팀 블로그에서 유일하게 만나실 수 있는 허접함의 향연이니, 나름 즐겁지 않으신가요. 어헣어헣.


- 자 언제나 그렇듯이 개념정리 부터 갑시다.

언제나 그렇죠. 제 글은 언제나 그래영. 우선 개념정리부터 하고 들어갑니당. 이 시리즈가 병렬적 C#에 대한 글이니, 우선 관련된 개념부터 정리하고 들어가야죠. 어헣. 이 바닥에서 흔히 사용되지만 쫌 헷갈리는 용어가 두 개 있죠. 바로, 동시성(Concurrency)와 병렬성(Parallelism)이죠. 여러분은 이거 잘 구분되시나영? 되시면 건너 뛰시구요, 안되면? 이 글은 바로 당신과 나를 위한 글인거죠. 아.. 이건 운명적 만남. 어헣.

이 개념을 잘 정리한 글을 찾아서 인터넷을 뒤졌는데, 그 결과 나름 정리한 결과는 다음과 같습니다.

1. 첫번째 정리(동시성 vs 병렬성)

동시성과 병렬성은 같은 의미가 아니다. 만약, T1과 T2라는 두 개의 작업이 시간상 언제 어떻게 수행될지 미리 정해져있지 않다면, 그 두 작업은 동시적이라고 말할 수 있다. 즉, 

  T1은 T2보다 빨리 수행되고 종료될 수 있다.
  T2는 T1보다 빨리 수행되고 종료될 수 있다.
  T1과 T2는 같은 시간에 동시에 실행될 수 있다.(이거슨 레알 병렬성.)
  T1과 T2는 교차적으로 수행될 수 있다.

2. 두번째 정리(동시적 vs 병렬적)

병렬성이라는 말은 여러개의 동일한 작업을 동시에 수행하는 것을 의미한다.(각각의 동시에 수행되는 동일한 작업들은 서로 독립적이다.) 동시성이라는 것은 여러개의 작업을 공통된 목표를 향해서 동시에 수행하는 것을 의미한다.

둘을 확실히 구분하기 힘든 것은, 동시성을 위해서 병렬성을 활용할 수 있다는 것이다. 예를 들어서 퀵소트 알고리즘을 생각해보면, 퀵소트 알고리즘의 각 분리된 단계는 병렬적으로 정렬될 수 있다. 하지만 전체 알고리즘은 동시적이다. 즉, 퀵소트 알고리즘은 동시적 이면서도(각 분할된 단계에서 나온 결과를 종합해서 하나의 공통된 목표를 추구하므로), 각 분할된 단계의 정렬은 병렬적일 수 있는 것이다. 그리고 병렬적인 각 단계의 정렬은 서로 무관하며, 서로 다른 데이터에 대해서 정렬을 하는 것이다. 그래서 좀 헷갈리긴 하겠지만, 전체 알고리즘을 병렬 퀵소트라고 부를 수도 있는 것이다.



- 넌 여전히 말이 많구나.

즉, 동시성과 병렬성은 서로 다른 개념이지만, 서로 같이 사용되는 경우가 있어서 확실히 구분하기가 애매한 경우도 있다는 말인데요. 어디 한번, 예제를 구경해보면서 이야기를 하시죠.

using System;
using System.Collections.Generic;
using System.IO;

namespace Exam1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> files = new List<string>();
            files.AddRange(Directory.GetFiles("C:\\음악", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Microsoft Visual Studio 9.0", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Windows Mobile 6 SDK", "*", SearchOption.AllDirectories));
            List<string> fileList = new List<string>();

            foreach (var file in files)
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Exists)
                {
                    if (fileInfo.Length >= 10000000)
                    {
                        fileList.Add(file);
                    }
                }
            }

            foreach (var file in fileList)
            {
                Console.WriteLine(file);
            }
        }
    }
}

<코드1> 일단 그냥 평범한 예제

<코드1>은 그냥 평범한 예제입니다. 명시해준 폴더에 있는 모든 파일을 검색해서 10메가가 넘는 파일의 목록을 만드는 프로그램이죠. 위 코드에서 왜 디렉토리를 저렇게 명시해줬냐고 물으신다면?!? 그냥 권한 때문에 접근 못하는 디렉토리가 있어서, 파일이 많은 디렉토리 위주로 골랐다고 대답해드리지요. 어헣. <코드1>을 동시성을 활용할 수는 없을까요? 만약에 동시성을 활용한다면, 어디를 동시적으로 실행 할 수 있을까요? 정답을 맞추시는 분께는 2박 3일로 하와이!!! 의 사진을 구경할 수 있는 기회를 드리겠습니다. 구글협찬이구요, PC는 개인지참입니다..... 아.. 상품대박.

foreach (var file in files)
{
    FileInfo fileInfo = new FileInfo(file);
    if (fileInfo.Exists)
    {
        if (fileInfo.Length >= 10000000)
        {
            fileList.Add(file);
        }
    }
}
<코드2> <코드1>에서 동시성을 활용가능한 곳!

넵, 바로 <코드2>가 동시성을 활용가능한 곳이지요. 왜냐면, 각 파일이름을 가지고 파일을 가져와서, 파일의 크기를 검사하는 각각의 작업은 서로 전혀 상관없기 때문이죠. '마재윤이조작이라니.jpg'라는 파일과 '박세식바보똥깨.avi'라는 파일에 대해서 동시에 작업이 진행된다고 해서 서로 겹치는 것도 없고, 문제가 될 것도 없습니다. 오히려, 순차적으로 수행할 때보다, 3-4개정도로 쪼개서 동시에 작업을 수행하면 훨씬 빨라지겠죠. 그래서! 여기서 병렬성을 활용하게 됩니다.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace Exam1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> files = new List<string>();
            files.AddRange(Directory.GetFiles("C:\\음악", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Microsoft Visual Studio 9.0", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Windows Mobile 6 SDK", "*", SearchOption.AllDirectories));
            List<string> fileList = new List<string>();

            Parallel.ForEach(files, (file) =>
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Exists)
                {
                    if (fileInfo.Length >= 1000000)
                    {
                        fileList.Add(file);
                    }
                }
            });

            foreach (var file in fileList)
            {
                Console.WriteLine(file);
            }
        }
    }
}

<코드3> 병렬성! 을 활용한 버전.

<코드3>에서 굵게 처리된 부분이 바로 변경된 부분입니다. 나머지는? 똑같죠잉~~. 어헣. 굵게 처리된 부분은 .NET 4.0에서 새로 추가된 부분이며, 추후에 더 자세하게 설명드릴 기회가 있을 것 같군뇨오! 그러니깐 일단 궁금증일랑 고이접어 나빌레라~ 하시고, <코드3>을 봅시당. <코드3>에서 분명 각 리스트를 몇 부분으로 쪼개서 수행시간을 줄이려고 병렬성을 도입했지만, 각 작업의 결과는 한 개의 리스트에 추가됩니다. 즉, 10메가 넘는 파일의 목록을 만든다는 공통의 목표를 달성하기 위해서 병렬성을 사용한 것이죠. 즉, 위 코드는 병렬성을 활용한 동시적 코드입니다. 이게 위에서 열심히 동시성과 병렬성을 설명드린 내용인 거죠.


- 그래서? 얼마나 빠른기고?

자~ 그럼 얼마나 빠른건지 어디 한번 확인해봅시다.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace Exam1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> files = new List<string>();
            files.AddRange(Directory.GetFiles("C:\\음악", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Microsoft Visual Studio 9.0", "*", SearchOption.AllDirectories));
            files.AddRange(Directory.GetFiles("C:\\Program Files (x86)\\Windows Mobile 6 SDK", "*", SearchOption.AllDirectories));
            List<string> seqFileList = new List<string>();
            List<string> parFileList = new List<string>();

            DateTime seqStart = DateTime.Now;
            //순차적 방식
            foreach (var file in files)
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Exists)
                {
                    if (fileInfo.Length >= 10000000)
                    {
                        seqFileList.Add(file);
                    }
                }
            }
            DateTime seqEnd = DateTime.Now;
            TimeSpan seqResult = seqEnd - seqStart;

            DateTime parStart = DateTime.Now;
            //병렬적 방식
            Parallel.ForEach(files, (file) =>
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Exists)
                {
                    if (fileInfo.Length >= 1000000)
                    {
                        parFileList.Add(file);
                    }
                }
            });
            DateTime parEnd = DateTime.Now;
            TimeSpan parResult = parEnd - parStart;

            Console.WriteLine("순차적 방식 : {0}",seqResult);
            Console.WriteLine("병렬적 방식 : {0}", parResult);
        }
    }
}

<코드4> 비교를 해보자!

<코드4>처럼 순차적 방식과 병렬적 방식의 시작 시간과 끝 시간을 기록해서 두 방식에서 걸리는 시간을 측정해봤습니다. 결과능~?

순차적 방식 : 00:00:01.6270960
병렬적 방식 : 00:00:00.6230368
계속하려면 아무 키나 누르십시오 . . .
<결과1> 비교 결과.

그렇쿤뇨. 병렬적 방식이 두배 이상 빠르게 나온 걸 볼 수 있습니다. 그저 기존의 루프를 병렬 루프로 바꾼 것 뿐인데 말이죠.


- 마무리 합시다.

일단 오늘은 기본적인 개념을 명확하게 하고, 아주 간략하게 예제를 봤습니다. 일단 .NET 4.0에서 병렬 프로그래밍이 상당히 편해진 거 같긴하죠? 조금씩 더 자세히 알아보도록 하지요. 어헣.


- 참고자료

1. http://blogs.sun.com/yuanlin/entry/concurrency_vs_parallelism_concurrent_programming
2. http://my.opera.com/Vorlath/blog/2009/10/08/parallel-vs-concurrent
3. Essential C# 4.0, Mark Michaelis, Addison Wisley

Welcome to Dynamic C#(21) - 인덱스의 힘.

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

- 인덱스는 왜 나오는 고냐...?

인덱스는 방대한 정보를 특정한 기준으로 잘 분류해 놔서 정보를 금방 찾을 수 있도록 해주는 고마운 장치이죠. 아무리 두꺼운 사전이 있다고 해도, 그 사전이 가나다 순이나 알파벳순으로 잘 인덱싱이 되어있지 않으면 쓸모없겠죠. C#도 그래서, 대화하기 여러운 COM과 잘 지내기 위해서 인덱스 가능한 프로퍼티를 사용하기 시작했쬬!


- 인덱스 가능한 프로퍼티가 몬데?

이름 그대로입니다. 그냥 이름 그대로 프로퍼티인데, 프로퍼티의 값을 가져올 때 인덱스로 특정 요소에 접근가능한 프로퍼티죠. 아래와 같은 모양이 인덱스로 접근하는 프로퍼티의 모습입니다.

myObject.MyIndexedProperty[index];

인덱스 가능한 프로퍼티는 내부적으로 그냥 간단한 접근자 메서드와 일반적으로 쓰이는 인덱서와 유사한 인덱서로 구성됩니다. 그냥 일반적으로 쓰는 인덱서처럼 생각할 수도 있죠. 그러면 일반적인 인덱서와의 차이점을 예제를 통해서 한번 확인해 보겠습니다. 우선 일반적인 인덱서의 예제를 보시죠.

class D
{
    private int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };

    public int this[int x]
    {
        get
        {
            return nums[x];
        }
    }
}

class C
{
    static void Main()
    {
        D d = new D();
        for (int i = 0; i < 8; i++)
        {
            Console.WriteLine(d[i]);
        }
    }
}


그리고 인덱스 가능한 프로퍼티의 사용예제입니다.

using System;
using Excel = Microsoft.Office.Interop.Excel;
using System.Collections.Generic;

namespace OfficeInteropExam2
{
    public class Account
    {
        public int ID { get; set; }
        public double Balance { get; set; }       
    }

    class Program
    {
        static void DisplayInExcel(IEnumerable<Account> accounts)
        {
            var excelApp = new Excel.Application();
            excelApp.Visible = true;
                       
            excelApp.Workbooks.Add();

            Excel._Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;

            workSheet.Cells[1, "A"] = "ID Number";
            workSheet.Cells[1, "B"] = "Current Balance";

            var row = 1;
            foreach (var acct in accounts)
            {
                row++;
                workSheet.Cells[row, "A"] = acct.ID;
                workSheet.Cells[row, "B"] = acct.Balance;
            }

            workSheet.Range["A1", "B3"].AutoFormat(
                Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2);
        }

        static void Main(string[] args)
        {
            var bankAccounts = new List<Account> {
                new Account {
                  ID = 345678,
                  Balance = 541.27
                },
                new Account {
                  ID = 1230221,
                  Balance = -127.44
                }
            };

            DisplayInExcel(bankAccounts);
        }
    }
}


전자는 간단하게 인덱서를 사용하는 예제이구요, 두번째는 엑셀에 데이터를 표시하는 COM 프로그래밍 예제입니다. 두번째 예제에서 밑줄친 부분이 바로 인덱스 가능한 프로퍼티를 사용한 부분입니다. 겉으로 보기에는 별 차이가 없어 보이는데요. 내부적으로는 어떨까요? 리플렉터로 우선 첫번째 예제를 보겠습니다.


위에서 붉은 선으로 표시된 대로, 일반적인 인덱서 앞에는 'Item'이라는 이름이 붙습니다. 그리고 이 인덱서를 사용하는 부분의 코드는,

private static void Main()
{
    D d = new D();
    for (int i = 0; i < 8; i++)
    {
        Console.WriteLine(d[i]);
    }
}

위와 같이 소스코드와 큰 변화가 없는 걸 볼 수 있습니다. 그러면 두번째 예제는 어떨까요? 인덱스 가능한 프로퍼티가 사용된 부분만 리플렉터로 보면,

workSheet.get_Range("A1", "B3").AutoFormat(XlRangeAutoFormat.xlRangeAutoFormatClassic2, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);

'Range["A1", "B3"]'이라고 썼던 부분이 'get_Range'라는 메서드 호출로 바뀐걸 보실 수 있습니다. 그러면 저 get_Range를 한번 찾아볼까요?


위에서 보시듯이 'Item'이라는 표시가 없습니다. 즉 일반적인 인덱서가 아니라는 이야기죠. 그리고 접근자 메서드를 가지고 있는 걸 보실 수 있습니다. 위에서 인덱스 가능한 프로퍼티가 '접근자 메서드와 일반적으로 쓰이는 인덱서와 유사한 인덱서로 구성'된다고 이야기 한 부분이 바로 이걸 가리키는 말이었던 거죠.


- 찝찝하지만..

인덱스 가능한 프로퍼티를 그동안 추가하지 않았던 이유는 C#팀의 디자인 철학과 맞지 않아서 였다고 합니다. C#팀에서는 'C라는 타입안에 있는 P라는 프로퍼티에 접근해서 값을 가지고 온다. 그리고 그 결과값에 대해서 인덱스로 접근한다'는 시나리오가 더 옳다고 생각한다고 하네요.

하지만, 인덱스 가능한 프로퍼티를 사용하는 COM이 무척이나 많이 퍼져있다는 게 문제였다고 합니다. 그래서 이전 포스트에서 언급 해드렸던 이유와 같은 이유로 COM 프로그래밍을 지원하기 위해서 이런 기능이 추가되었다고 하네요.

비록 인덱스 가능한 프로퍼티를 사용할 수 있긴 하지만, C#에서는 인덱스 가능한 프로퍼티를 선언할 수는 없습니다. COM 라이브러리가 tlbimp라는 툴을 이용해서 만들어 지기 때문에, C#에서는 만들수 없다고 하네요. 즉 C#에서는 COM 라이브러리에 대해서만 인덱스 가능한 프로퍼티를 사용할 수 있는 것이죠.

컴파일러는 COM 타입에서 인덱스 가능한 프로퍼티로 보이는 걸 모두 임포트해둔다고 합니다. 그래서 해당 프로퍼티에 대해서는 이름을 붙인 인덱서 문법을 이용해서 사용할 수 있도록 한다고 하네요. 즉, '컴파일러는 이름을 붙인 인덱서 문법을 통해서 인덱스 가능한 프로퍼티를 사용할 수 있도록 한다'는 것이죠. 위의 예제에서 보여드렸듯이 일반적인 프로퍼티는 접근자 메서드를 통해서 바로 접근할 수 없습니다. 하지만 문법적으로는 매우 유사한 형태를 취하면서 내부적으로는 다른 일이 벌어지고 있는 것이죠.


- 오버로드 판별도 해보자.

인덱스 가능한 프로퍼티가 추가되면서 또 오버로드 판별에 변수가 생겼습니다. 아래의 예제를 가지고 설명을 해보면요,

myObject.MyIndexedProperty[index];

우선 컴파일러가 이 구문을 보게 되면, '.'왼쪽을 그 객체의 타입과 연결시킨다고 합니다. myObject의 타입이 예를 들어서 MyType이라고 하면, 그 둘을 연결시키는 것이죠. 그 다음에는 MyIndexedProperty라는 이름을 MyType에서 쭉 훑어보면서 찾습니다.

하위 호환성을 유지하기 위해서, 컴파일러가 만약 검색중에 같은 이름을 가진 평범한 프로퍼티를 보더라도 무조건 그 프로퍼티와 호출을 바인딩하게 됩니다. 하지만, MyType에서 MyIndexedProperty라는 프로퍼티를 발견했다고 해도, MyType에 인덱서가 없다면 이 프로퍼티에 호출을 바인딩하지는 않습니다.

이렇게 인덱스 가능한 프로퍼티를 제외한 일반적인 바인딩이 실패 했다면, 컴파일러는 MyType이 ComImport타입인지 확인합니다. ComImport타입이 맞다면, MyType에서 사용가능한 인덱스 가능한 프로퍼티의 리스트를 만들어서, 후보군에 대해서 오버로딩 판별을 하는데, 이때의 판별 알고리즘은 평범한 프로퍼티와 동일하게 진행됩니다. 그저 이름으로 인덱서를 취급하듯이 하는 것이죠.

그리고 인덱스 가능한 프로퍼티를 사용했다고 해서 특별한 일이 일어나는 건 아닙니다. 그저 예전버전에서 값을 가져오려면 적어야 했던 내용을 그대로 생성해주는 syntactic sugar일 뿐이기 때문이죠. 이 부분은 아래의 예제를 보시면 좀 더 명확해 집니다.

//인덱스 가능한 프로퍼티를 사용한 경우
workSheet.Range["A1", "B3"].AutoFormat(
    Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2);

//기존버전에서 작업해야 했던 경우
workSheet.get_Range("A1", "B3").AutoFormat(Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing);

위에서 보여드렸던 예제중에서 인덱스 가능한 프로퍼티를 사용한 코드를 리플렉터에서 보면 아래와 같았습니다.

workSheet.get_Range("A1", "B3").AutoFormat(XlRangeAutoFormat.xlRangeAutoFormatClassic2, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value);

똑같죠? 넵. syntatic sugar일 뿐이니까요. 낄낄낄.


- 마치면서

이번 포스트는 내용을 정리하면서 좀 힘들었습니다. 내공의 부족 때문인지 내용이 제대로 이해가 안됐기 때문이죠. 그런데 예제를 만들고 확인해보고 하니깐 조금씩 이해가 되더군요. 정리가 잘 된건지 모르겠습니다. 그럼 오늘은 여기까지~~!!!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/11/03/com-interop-in-c-4-0-indexed-properties.aspx
2. http://msdn.microsoft.com/en-us/library/dd264733%28VS.100%29.aspx

Welcome to Dynamic C#(20) - 어르신과 대화하는 법.

C# 2010. 5. 17. 09:00 Posted by 알 수 없는 사용자
- 어르신과 대화하려면 어케 해야 되는거임?

간단합니다. 말씀하시는 내용을 잘 경청하고, 대꾸는 딱 필요한 만큼만 하면 되는 거죠. COM은 아마 그동안 C#이 못마땅 했을 겁니다. VB.NET은 공손하게 필요한 말만 딱 하는데, C#은 'Type.Missing'이 어쩌고 저쩌고 주저리주저리 말이 많았기 때문이죠. C#도 이제 99년도 부터 출발했다고 볼때, 11년이나 되었으니 꽤 성숙한 셈이죠. 이제 예의를 갖추기 시작한 겁니다. 낄낄낄.


- C#의 버릇없던 옛 시절.

그러면 C#얼마나 버릇이 없었는지 아주 간단한 예제를 통해 알아보시죠.

using System;
using Word = Microsoft.Office.Interop.Word;

namespace ConsoleApplication3
{
    class Program
    {
        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            object useDefaultValue = Type.Missing;

            wordApp.Documents.Add(ref useDefaultValue, ref useDefaultValue,
                ref useDefaultValue, ref useDefaultValue);
        }

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


위 예제는 그냥 오피스 워드 창을 하나 띄우는 예제입니다. 그런데 평소에 오피스 COM 프로그래밍을 접해보지 못한 분이라면, 뭔가 특이한 점을 발견하셨을 겁니다. 'Type.Missing'같은 독특한 걸 object형 변수에 넣고, Add메서드에 여러번 반복해서 써넣기 때문이죠. 딱히 값을 리턴받아서 뭘 하는 것도 아닌 것 같고, Missing이라면 뭔가 없다는 것 같은데, 없는 값을 왜 저렇게 반복해서 넣어야 할까... 하는 생각이 드는 것이죠. 이제 COM이 왜 C#을 싫어했는지 아시겠나요? 말이 많았거든요.

지난 포스트에서 보셨듯이, C# 4.0은 다른 런타임과의 상호운용에 신경을 무척 많이 썼으며, 그중의 하나가 COM과의 상호운용이었습니다. C# 4.0에서는 COM과 대화할 때 좀더 말을 적게 하면서 예의를 갖춰서 대화를 하게 된 것이죠. 컴파일러가 COM 객체를 대상으로 작업하고 있다는 걸 눈치채는 순간, 컴파일러는 매개변수에 'ref'키워드를 안붙이고 메서드나, 인덱서, 프로퍼티에 넘길 수 있도록 해줍니다.

using System;
using Word = Microsoft.Office.Interop.Word;

namespace OfficeInteropExam2
{
    class Program
    {
        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            object useDefaultValue = Type.Missing;

            wordApp.Documents.Add(useDefaultValue,
                useDefaultValue, useDefaultValue, useDefaultValue);
        }

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


즉 C# 4.0에서는 위 처럼 ref를 빼고 작업할 수 있도록 도와줍니다. 그리고 컴파일러가 나중에 각 매개변수 앞에 ref를 붙여서 컴파일 하는 것이죠. 일종의 syntactic sugar인 것입니다. 그런데 여기에 지난 포스트까지 설명드렸던 Named and Optional Arguments를 이용하면 아예 매개변수를 생략할 수 있습니다.

using System;
using Word = Microsoft.Office.Interop.Word;

namespace OfficeInteropExam2
{
    class Program
    {
        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            wordApp.Documents.Add();
        }

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


지난 포스트까지 설명드렸듯이 아예 매개변수를 생략하면 기본값으로 설정된 값이 넘어가게 됩니다. 그러면, Add메서드의 각 파라미터에는 기본값이 어떻게 설정되어 있을까요? Add메서드의 정의를 보면 아래와 같습니다.

Document Add(ref object Template = Type.Missing, ref object NewTemplate = Type.Missing, ref object DocumentType = Type.Missing, ref object Visible = Type.Missing);

위에서 보시듯이, 기본값은 'Type.Missing'으로 설정되어 있습니다. 즉 매개변수를 생략하고 Add메서드를 호출하면, 기본값으로 Type.Missing이 넘어가고 컴파일러는 거기에 자동으로 ref를 붙여서 호출을 완성시켜 주는 것이죠. 지금은 겨우 파라미터가 4개정도라서 감흥이 없으실지도 모르겠지만, 파라미터가 30개정도 되는 메서드들을 자주만나다보면 아마 이 기능이 너무나도 고맙게 느껴지시겠죠? ㅋㅋㅋㅋ


- 마치면서

오늘은 아주 짧게 향상된 COM 프로그래밍에 대해서 알아봤습니다. COM이 안쓰이길 바랬음에 불구하고 많이 쓰이니 어쩔 수 없이 C# 4.0에서 COM에 대한 지원이 많이 들어갔다고 하는걸 보니, 역시 기술의 생명은 벤더에게만 달린게 아니라는 생각을 해봅니다. 그럼 오늘은 여기까지 하고 다음에 또 뵙죠!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/06/16/com-interop-in-c-4-0.aspx
2. http://msdn.microsoft.com/en-us/library/dd264733%28VS.100%29.aspx

Welcome to Dynamic C#(19) - 위너 고르기.

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

- 위너를 고르는 방식!

위너라니, 무슨 위너이야기 일까요? 넵. 메서드 오버로딩에서 호출에 맞는 메서드를 고를때, 어떤 경우에 어떤 메서드가 더 적합한지 고르는, 즉 메서드 오버로딩 중에서 위너를 고르는 거에 대한 이야기 입니다. 아마도 이런 경우는 생각보다 자주일어날 것 같은데요. 과연 컴파일러는 어떤 방식으로 위너를 고를까요? 키로? 얼굴로? 능력으로? 한번 알아보시죠.


- 첫번째 경우

일단 두 메서드의 파라미터 개수가 같다고 할때는 매개변수가 어떤 타입의 파라미터로 형변환하는게 더 나은것인지를 기준으로 판단합니다.

class C
{
    public void Foo(object o) { Console.WriteLine("object"); }
    public void Foo(int x) { Console.WriteLine("int"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10);
    }
}


그럼 위의 예제에서는 누가 위너가 될까요? 10은 정수니까, object로 형변환도 가능하고, int로도 형변환이 가능합니다. 하지만 정수는 object보다는 int로 형변환 되는게 더 적합한 형변환이죠. 그래서 여기서는 'Foo(int x)'가 위너가 됩니다. 'Foo(int x)' ㅊㅋㅊㅋ


- 두번째 경우

두번째 경우는 파라미터 배열이 끼어듭니다.

class C
{
    public void Foo(int x, int y) { Console.WriteLine("int twins"); }
    public void Foo(params int[] x) { Console.WriteLine("params"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10, 20);
    }
}


첫번째 예제를 약간 변형시킨 건데요. 컴파일러는 파라미터 배열을 보자마자 이 파라미터 배열을 확장시켜서 메서드의 시그니처를 'Foo(int x_1, int x_2)'같이 만들고 이 시그니처를 가지고 오버로딩의 후보군에 끼워넣는다고 합니다. 그런데 컴파일러가 파라미터 배열을 확장만 시키는게 아니라, 파라미터 배열에서 확장되었음을 표시한다고 하네요. 그리고 파라미터에서 확장된 시그니처의 경우는 오버로드 판별에서 2등급으로 취급된다고 합니다. 즉 메서드 시그니처가 동일할 경우에 파라미터 배열은 2등급이기 때문에, 다른 일반적인 1등급 파라미터에 우선순위에서 밀리게 된다고 합니다. 위의 예제에서는 'Foo(int x, int y)'가 위너네요. ㅊㅋㅊㅋ


- 세번째 경우

이제 드디어 지금까지 열심히 이야기 해온 옵셔널 파라미터가 끼어들 차례입니다.

class C
{
    public void Foo(int x) { Console.WriteLine("int"); }
    public void Foo(int x, int y = 0, int z = 10)
    { Console.WriteLine("optionals"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10);
    }
}


위 예제는 쫌 난감합니다. 'c.Foo(10)'라는 호출만 보자면, 둘다 똑같이 해당되기 때문이죠. 첫번째 Foo는 시그니처가 완전히 일치하고, 두번째 Foo도 x를 제외한 값은 모두 기본값이 사용될 수 있으니까요. 이 경우에는 파라미터 배열과 같이 옵셔널 파라미터를 2등급으로 취급한다고 합니다. 즉 첫번째 Foo메서드가 옵셔널 파라미터가 하나도 없기 때문에 첫번째 Foo가 위너가 되는 것이죠. 그런데 만약에 첫번째 메서드에도 옵셔널 파라미터가 있다면 어떻게 될까요?

class C
{
    public void Foo(int x, int y = 0) { Console.WriteLine("optional1"); }
    public void Foo(int x, int y = 0, int z = 10)
    { Console.WriteLine("optional2"); }

    static void Main(string[] args)
    {
        C c = new C();
        c.Foo(10);
    }
}


위와 같이 작성된 경우 말이죠.


이런 에러를 보게됩니다. 즉, 둘간의 차이를 분별해낼 수 없기 때문에 모호한 호출이라는 것이죠.


- FAQ!

옵셔널 파라미터는 그동안 아주 꾸준히 C#에 추가해달라고 요청하던 기능입니다. 특히 오피스같은 COM과 연동하는 작업을 하는 프로그래머들이 많이 요청을 했었습니다. 왜 이런 기능을 예전에 안하고 지금하느냐? 하는 질문이 있을 법한데요. C#개발팀의 Sam Ng가 답변한 내용을 옮겨볼까 합니다.

1. 왜 이걸 좀 더 일찍하지 않았냐?

- 왜 이걸 좀 더 일찍 하지 않았느냐 하면 말이죠. 우린 진짜 이 기능이 C#에 포함되지 않았으면 했습니다. 이걸 그동안 계속해서 미뤄온 건, 이건 우리가 원했던 패러다임이 아니었기 때문이었죠.

2. 그럼 왜 지금은 이걸 추가했느냐?

- 이게 다 COM 때문이죠. 진짜 이건 사라지지를 않더라구요! 이걸 없앨려고 노력했지만, 사람들은 계속 이걸 사용하고 있고, 앞으로도 계속 사용하려고 하더군요. C#이랑 COM이랑 무슨 관계냐구요? 오피스. 오피스 PIAs때문이죠. 오피스 PIAs는 대부분 30개정도의 파라미터를 갖는 메서드로 이루어져 있죠. 그 파라미터의 대부분은 옵션이구요. 대부분의 경우에는 한개정도의 매개변수만 적어주고 나머지는 다 기본값을 사용하면 되는거죠.

이제 Named and Optional Parameters를 통해서 옵션인 파라미터는 안적고도 메서드를 호출할 수 있죠. 오피스 메서드를 호출할때도 모든 매개변수마다 Type.Missing같은거 안적어주고도 호출할 수 있는거죠. 그리고 매개변수에 해당하는 파라미터 이름을 적을 수 있기 때문에, 딱 필요한 거만 매개변수로 넘겨주고, 나머지는 생략할 수 있죠.

그리고 ref 없이 COM을 사용할 수 있도록한 기능과 조합해서 사용하면 COM 코드가 더 간결해지고 지루한 작업은 매우 줄어들겁니다. 컴파일러가 옵션인 ref 파라미터에 넘겨줄 임시값을 만들어서 매개변수를 넘겨주거든요.

제가 예전에 이야기 했듯이 C# 4.0의 큰 테마중의 하나가 다른 런타임(COM, 동적언어 등)과의 상호운용이에요. 그런 테마가 이런 기능을 반드시 갖추도록 했던거죠.


- 마치면서

아~ 이제 Named and Optional Parameters에 대해서 할말은 다 한것 같습니다. 자료를 읽으면서 저도 많이 배우고 재밌는 내용도 많이 읽었네요. 여러분은 어떠셨나요? 호호호호-_- 그럼 오늘은 여기까지 하고~ 다음에 또 다른 이야기 가지고 오겠습니다!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/04/17/named-and-optional-arguments-ties-and-philosophies.aspx

Welcome to Dynamic C#(18) - 이름을 붙이면서 벌어진 일들.

C# 2010. 5. 10. 09:00 Posted by 알 수 없는 사용자
- 무슨일인데 그러냥?

네. 언제나 기존의 질서안에서 새로운 변화를 가져오려고 하면 새로운 문제들이 생기기 마련이죠. 오늘은 매개변수에 이름을 붙이면서 생겨난 문제와 내용에 대해서 설명을 드려보려고 합니다. 우끼끼끼!!


- 이름은 다 어디서 가져오놔?

우선, CLR이 파라미터 이름은 메서드 시그니처의 일부로 보지 않기 때문에 A라는 메서드를 오버라이드해서 B라는 메서드를 작성한다고 할때, A와 B의 파라미터 이름이 다르다고 해도 전혀 문제되지 않습니다.

public class Animal
{
    public virtual void Eat(string foodType = "Rice")
    {
    }
}

class Monkey : Animal
{
    public override void Eat(string bananaType = "Rainbow Banana")
    {           
    }

    static void Main(string[] args)
    {
        Monkey m = new Monkey();
        Animal a = m;

        m.Eat(bananaType: "Black Banana");
        a.Eat(foodType: "Hamburger");
    }
}


위의 예제를 보시면, Animal을 Monkey가 상속해서 Eat을 오버라이드 하고 있습니다. 하지만 메서드 간의 파라미터 이름은 전혀 문제가 되지 않습니다. 그런데요, 이름이 틀리게 되면 또 하나의 의문점이 생길 수 있습니다. 언제 어떤 이름이 쓰이는 걸까요? 해답은 생각보다 간단합니다. 수신자를 중심으로 생각하는 것이죠.

여기서 수신자란 메서드의 호출대상이 되는 객체를 말하는데요. 'm.Eat()'에서는 m이 수신자가 되는 것이죠. 즉, 'm.Eat'에서 m은 정적타입인 Monkey타입의 객체입니다. 그래서 m.Eat에서는 Monkey클래스에 정의된대로, 'bananaType'을 파라미터의 이름으로 가져옵니다. 그리고 a.Eat에서는 Animal클래스에 정의된대로, 'foodType'을 파라미터의 이름으로 가져오는 것이죠. 주의깊게 보셨다면, m에서 생성한 객체를 그대로 a에 넣어주는 걸 보실 수 있습니다. 즉, 동일한 객체라는 의미인데요. 동일한 객체에 대해서라도, 수신자를 중심으로 파라미터의 이름을 가져온다는 이야기가 되는거죠.


- 이름을 가져올 때 안에서 벌어지는 일.

class Calc
{
    static double CalcRatio(double source,
        double factor1 = 0.87,
        double factor2 = 1.0)
    {
        return source * factor1 * factor2;
    }

    static void Main(string[] args)
    {
        CalcRatio(92.1, factor2:1.11);
    }
}


위와 같은 코드를 가지고 생각을 해보겠습니다. 실제로 위 코드가 실행될 때까지 어떤 일이 벌어지는지 말이죠. 우선 컴파일러가 'CalcRatio'를 호출하는걸 보게되면, 이름을 붙이지 않은 매개변수가 이름을 붙인 매개변수보다 앞쪽에 있는지 확인을 합니다. 그리고 매개변수에 붙인 파라미터의 이름이 중복되지 않는지 확인합니다. 같은 파라미터에 두개의 매개변수를 넘길수는 없으니 말이죠. 그리고는 적용가능한 모든 후보메서드군을 생성합니다. 위의 예제에서는 딱 한개뿐이죠. 그 후에는 각각의 후보메서드에 대해서 몇가지 검사를 합니다.

일단 메서드 호출에 적혀있는 파라미터의 이름이 후보군에 있는 메서드의 파라미터 이름과 동일한지 검사합니다. 여기서는 'factor2'의 이름을 각 후보메서드가 파라미터로 가지고 있는지 확인하겠죠. 그리고 이름이 붙은 모두 매개변수와 파라미터가 일치하는지 확인합니다. 즉, CalcRatio의 파라미터 중에 이름이 붙지 않은 매개변수를 받지 못한 factor1, factor2는 이름이 붙은 매개변수를 받아야 한다는 것이고, 같은 파라미터에 중복되는 매개변수가 없어야 한다는 것이죠.

만약에 이름이 붙은 매개변수나 이름이 붙지않은 매개변수 어느 것도 받지 못한 파라미터가 있다면, 컴파일러는 그 파라미터가 옵셔널 파라미터인지 검사합니다. 만약에 그 파라미터가 옵셔널 파라미터라면, 기본값을 파라미터에게 넘겨줄 매개변수로 사용하게 됩니다.

이런과정을 겨처서 매개변수 목록이 정리되면, 컴파일러는 늘 하듯이, 각 매개변수가 형변환에 문제가 없는지 확인을 합니다. 위의 예제에서 정리된 매개변수의 목록은 ['92.1', '0.87', '1.11']가 되겠죠.

이 모든 과정은 철저하게 컴파일 시점에서 벌어지는 'syntactic sugar'입니다. syntactic sugar는 그저 프로그래머의 수고를 덜어주는 역할을 하는 기능을 뜻하는 데요, 지금까지 설명드린 'Named and Optional Parameters'는 새로운 참조를 요구하지도 않고, 새로운 호환성 문제를 만들지도 않습니다. 생성된 IL을 보면, 그냥 일반적으로 호출하는 모양과 차이가 없기 때문이죠. 즉, 컴파일러가 위에서 설명드린 과정을 거쳐서 정리된 매개변수의 목록을 만들고 나면, 프로그래머가 원래 똑같은 매개변수목록으로 메서드를 호출한 것 처럼 처리를 합니다. 그래서 컴파일이 되고 난 후에, 메서드의 파라미터 이름이 바뀌거나 새로운 옵셔널 파라미터가 추가되어도 아무문제 없이 동작하는 것이죠.


- 중요한 거 한가지만 더!! 캬캬캬

class Calc
{
    static int GetNum1()
    {
        Console.WriteLine("GetNum1");
        return 1;
    }

    static int GetNum2()
    {
        Console.WriteLine("GetNum2");
        return 1;
    }

    static int GetNum3()
    {
        Console.WriteLine("GetNum3");
        return 1;
    }

    static void DoSth(int num1, int num2, int num3)
    {
    }

    static void Main(string[] args)
    {
        DoSth(num3: GetNum3(),
        num1: GetNum1(),
        num2: GetNum2());
    }
}


위와 같은 예제가 있다고 했을때요, 아마도 컴파일러는 매개변수의 순서를 재정렬해서 num1, num2, num3의 순서로 각 파라미터에 넘겨줄 것 같은데요. 메서드안의 GetNum시리즈는 어떤 순서로 평가될까요? 써있는 순서대로 앞에서 뒤로 할 것같다고 생각하셨다면 정답! 입니다. 처음에 GetNum3, GetNum1, GetNum2의 순서로 말이죠. 결과를 보시면 명확합니다.


내부적으로는 각 파라미터에 대한 표현식의 결과를 저장할 공간을 임시로 만들고, 각 표현식의 결과를 저장한 후에, 그 임시값들을 순서에 맞게 재정렬해서 파라미터에게 넘겨준다고 합니다. 재밌지 않나요? 저만 그런가효? 호호호호-_-;;;;


- 마치면서

이제 Named and Optional Parameter(도대체 한글로 뭐라고 써야할지 감이 안잡히네요-_-)에 대해서 기본적인 이야기는 한 것 같은데요. 처음에는 '그냥 파라미터에 기본값을 줄 수 있고, 매개변수를 넘겨줄 때 순서를 바꿔서 줄 수도 있다' 이정도 인줄 알았는데, 공부를 하다보니 생각보다 복잡하기도 하고 재미있는 내용이 많아서 글로 정리하면서도 즐거웠습니다. 오늘은 여기까지 하죠~~~~~!


- 참고자료

1.
http://blogs.msdn.com/samng/archive/2009/04/01/named-arguments-and-overload-resolution.aspx

Welcome to Dynamic C#(17) - 필요한 말만 하고 삽시다.

C# 2010. 5. 7. 09:00 Posted by 알 수 없는 사용자
- 이젠 dynamic을 벗어나서!

문득 제가 거의 1년 가까이 dynamic만 이야기를 했다는 걸 깨달았습니다.-_-;;;; C# 4.0에 dynamic만 추가된게 아닌데 말이죠;;; 반성을 하면서! 이제 dynamic말고 다른 이야기를 좀 하겠습니돠.


- 파라미터에 기본값을 설정하는거지.

프로그래밍을 하다보면, 여러가지 파라미터를 가지는 메서드를 작성하는 경우가 많은데요. 호출시에 꼭 매번 넘겨줘야 하는 파라미터가 있는가 하면, 대부분의 경우 그냥 한가지 값으로만 쭉~ 사용하는 파라미터도 있습니다. 그래서 C#에서는 늘~ 메서드 오버로딩을 통해서 파라미터가 축약된 메서드를 정의해주고, 그 메서드 안에서 원래의 메서드를 호출하면서, 기본값을 매개변수로 넘겨주는 형태를 취했습니다. 그래서 많은 프로그래머들이 메서드를 정의할 때, 파라미터에 기본값을 정의할 수 있게 해달라고 요청했습니다. 그리고 그 메서드를 호출할 때, 기본값을 그대로 쓰는 경우에는 매개변수를 생략할 수 있게 해달라고 말이죠.

C# 4.0에서 파라미터에 기본값을 설정하는데는 두가지 방법이 있는데요,

class C
{
    static int Add(
        [DefaultParameterValueAttribute(10)] int num1,
        int num2 = 30)
    {
        return num1 + num2;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(Add(15));
    }
}


위의 코드를 보시면, num1에는 DefaultParameterValueAttribute라는 어트리뷰트가 붙어있는 걸 보실 수 있는데요. 이건 그냥 하위호완성과 COM, VB와의 상호운용을 위해서 만들어진거라고 합니다. 이 방법은 권장되지 않는데요, 왜냐하면 이 어트리뷰트는 특정 파라미터에 기본값이 있다는 건 명시하지만, 그 파라미터가 옵션인지 아닌지는 명시할 수 없기 때문입니다. 즉 컴파일러 조차 이 파라미터가 옵션으로 생략가능한지 인식하지 못합니다. 그래서 결국에는 명시된 기본값은 아예 사용되지도 못하는 거죠.

그리고 'num2' 파라미터를 보시면 이 부분은 두가지 의미가 있습니다. 첫째는 컴파일러에게 이 파라미터가 옵션으로 생략가능하다는 걸 알려주는 거구요, 둘째는 프로그래머가 이 파라미터에 아무 값도 안 넘겨주때 사용할 수 있는 기본값을 컴파일러에게 알려주는 것입니다. Add메서드를 호출할 때 인텔리센스를 보면요,


위에서 말씀드린대로, 'num1' 파라미터에 대해서는 기본값은 설정이 되어있지만, 옵션이라는 표시가 없기 때문에 생략할 수 없습니다. 그렇기 때문에 어트리뷰트에 명시된 기본값은 아예 사용되지 않는 것이구요. 그리고 'num2' 파라미터를 보시면, 기본값이 30이라고 표시되는 걸 보실 수 있습니다. 그리고 이렇게 표시된 파라미터는 옵션으로 생략가능한 것이구요. 위의 코드를 실행하면, 15와 'num2' 파라미터의 기본값인 30이 더해져서 45라는 결과가 나오게 됩니다.


- 파라미터에 기본값을 설정하면 어케 되는고얌~?

위의 Add메서드를 리플렉터에서 보면 아래와 같습니다.

private static int Add([DefaultParameterValue(10)] int num1,
                              [Optional, DefaultParameterValue(30)] int num2)
{
    return (num1 + num2);
}

'num1' 파라미터는 위에서 선언해준 어트리뷰트가 그대로 설정되어 있는 걸 보실 수 있구요. 'num2' 파라미터를 보시면, 기본값을 설정하는 게 실제로는 두가지 일을 한다는 걸 알 수 있습니다. 파라미터에 DefaultParameterValue 어트리뷰트를 통해서 기본값을 설정하고, 이 파라미터가 옵션으로 생략가능함을 나타내는 Optional이라는 표시도 하게 되는 것이죠. 이런 두가지 어트리뷰는 이미 CLR에 존재했다고 합니다. VB.NET에서는 이미 제공되던 기능이니깐 당연한 이야기겠죠.

여담이지만, VB.NET과 C#개발팀이 통합되었다고 합니다. 서로 같이 크면서 동일한 기능을 제공하고자 하는 'coevolution'전략을 위함이라고 하는데요. 그 덕분일까요? C#에도 오늘 소개해드리는 기본값을 설정하는 기능이 추가되었고, VB.NET에도 C# 3.0에서 추가되었던 '컬렉션 이니셜라이저'나 '자동으로 구현된 속성'같은 기능이 추가되었습니다. 자세한 내용은 여기를 참고하시면 되겠네요.

그런데 기본값을 설정할 때, 몇가지 규칙이 있는데요. 첫째로는 옵션으로 생략가능한 파라미터는 생략불가능한 파라미터를 모두 선언한 뒤에 나와야 합니다.


위와 같이 옵션인 파라미터가 먼저 나오고, 뒤에 반드시 필요한 파라미터를 선언하면, 옵셔널 파라미터는 반드시 꼭 필요한 파라미터들 뒤에 나와야 한다고 경고메세지가 뜨는 걸 볼 수 있습니다. 그리고 ref나 out으로 설정된 파라미터는 기본값을 설정할 수 없습니다. 왜냐하면, ref나 out에 대응되는 상수값이 존재하지 않기 때문이죠.

그리고 호출시에 옵셔널 파라미터로 설정된 파라미터에 매개변수를 넘겨주지 않으면, 컴파일러는 DefaultParameterValue 어트리뷰트에 설정된 기본값을 가지고 와서, 그 값을 매개변수로 해서 메서드를 호출하는데 사용하게 됩니다.


- 매개변수에게 이름을 허 하여뢋!

형을 형이라고 부르지 못하고, 아버지를 아버지라 부르지 못한 건 아니지만, 매개변수는 늘 이름없는 설움을 겪어야 했습니다. 파라미터는 이름이라도 갖고 있었죠. 태어나서 이름하나 세상에 남기지 못하는게 얼마나 슬픈....여기까지 하고 이야기 계속 하겠습니다.-_-;

이제 이름붙인 매개변수를 사용하게 되면, 이 모든 장점을 제대로 활용할 수 있게 됩니다. 만약에 모든 파라미터가 옵셔널 파라미터 라면, 그 중에 값을 넘겨주고 싶은 것들만 이름을 붙여서 매개변수를 넘겨줄 수 있는 것이죠. 기존의 오피스등의 COM 프로그래밍을 할 때, 대부분의 값들이 옵션으로 생략하능한 파라미터지만, C#에서는 그것들을 생략할 방법이 없어서 의미도 없는 값을 반복해서 넘겨줘야 했던 걸 생각하면 굉장히 편해질거라는 생각도 드네요.


이름붙인 매개변수는 메서드를 호출할 때 사용되는 데요. 예제를 보시면요,

class C
{
    static double CalcRatio(
        double fact1 = 90.0,
        double fact2 = 0.9887,
        double fact3 = 33.211)
    {
        return fact1 * fact2 * fact3;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(
            CalcRatio(fact1:40.12, fact3:13.11)
            );
    }
}


위의 예제를 보면, 세상에 이런 엉터리 계산식이 있을진 모르겠지만 아무튼 뭔가의 비율을 계산하는 메서드인 CalcRatio가 있습니다. 계산할 때 일반적으로 고정된 상수들이 있을 수 있는데요, 그런 경우를 위해서 기본값을 설정해두었습니다. 그리고 호출하는 부분은 보시면, 'fact1', 'fact3' 파라미터에 넘겨질 값들에 각각 이름을 붙인 것을 볼 수 있습니다. '40.12'는 'fact1'파라미터에 넘겨질 매개변수이고, '13.11'은 'fact3'파라미터에 넘겨질 매개변수 인 것이죠.

매개변수에 이름을 붙이는 건, 꼭 기본값이 있는 옵셔널 파라미터에만 한정되는 않습니다.

class C
{
    static double CalcRatio(
        double superfactor,
        double fact1 = 90.0,
        double fact2 = 0.9887,
        double fact3 = 33.211)
    {
        return superfactor * (fact1 * fact2 * fact3);
    }

    static void Main(string[] args)
    {
        Console.WriteLine(
            CalcRatio(fact1:(40.12 / 2), fact3:13.11, superfactor:100)
            );
    }
}


위의 예제와 같이 기본값이 없는 일반 파라미터에 넘겨줄 매개변수에도 이름을 붙일 수 있으며, 매개변수의 순서는 파라미터의 순서와는 전혀 상관없이 배열할 수 있습니다. 그리고 'fact1'의 매개변수처럼 원래 매개변수로 넘겨줄 때 할 수 있는 것 처럼 아무 표현식이든지 올 수 있습니다.


- 마치면서

그동안 아주 오랫동안.... 게으름과 겹치면서 너무 오랫동안 dynamic에 대해서만 이야기를 해왔는데요. 문득 돌아보니 dynamic외에도 다룰 내용이 좀 더 있다는 걸 깨달았습니다.-_-;;;; 머리가 둔하면 이렇죠. 하하하하하>ㅇ<


- 참고자료

1. http://blogs.msdn.com/samng/archive/2009/02/03/named-arguments-optional-arguments-and-default-values.aspx

Welcome to Dynamic C#(16) - dynamic이라도 이건 안되는 거임.

C# 2010. 5. 5. 09:00 Posted by 알 수 없는 사용자
- Long time no see~

오랜만이죠~? 다행히 기다려주신 분이 없는 거의 없는 관계로 마음은 불편하지 않았습니다. 그런데 왜 눈물이 아흙.... 아직 못한 이야기가 조금 있는거 같아써! 조금 더 이야기를 하도록 할께영~ 호호호호-_-


- 상황1. dynamic타입의 변수에 들어있는 값을 변환시키기

다음과 같은 코드가 있다고 가정을 했을 때요,

static void Main()
{
    dynamic d = 10;
    d++;
}

어떤 일이 벌어질까요? d에는 11이라는 값이 있어야 할 것 같지만, dynamic타입은 실제로는 object타입이기 때문에 다른 결과가 나옵니다. dynamic타입이 실제로는 object라는 건 이전에 이야기 했던 내용인데요, 자세한 내용은 이전포스트를 참고하시면 되겠습니돠. 아무튼 처음에 d에 10을 넣을 때, int에서 object로 박싱이 일어나구요, 두번째 줄에서 d에서 언박싱한 값을 가지고 ++연산을 수행합니다. 하지만 이 값은 다시 박싱되어서 저장되지는 않는다는 게 문제입니다. 그러면 결과는 여전히 10을 가리키겠죠.

그런데 이런 문제는 런타임 바인더의 구조 덕분에 해결이 가능했다고 하는데요. 이전에 말씀드렸듯이 런타임 바인더는 동적인 구문을 적절한 객체와 연산으로 바인딩하고 그 결과를 Expression Trees의 형태로 DLR에게 리턴해줍니다. Expression Trees의 장점은 목표로 하는 형태로 변환되기 전에, 여러가지 변환이나 최적화가 용이하다는 점이 었는데요, Expression Trees에 박싱된 값을 언박싱하고 값을 변화시키는 요소가 있고, 그 값을 다시 박싱해서 저장하는 것도 있다고 합니다. 그래서 이런형태의 동적인 표현식을 제대로 처리할 수 있다고 하네요.


- 상황2. 중첩된 구조체 연산

이번 문제는 조금 더 알쏭달쏭한데요. '.'으로 여러번 연결된 표현식을 생각해보면요, 각각 부분별로 쪼개셔서 바운딩이 됩니다. 즉 A.B.C.D같은 표현식이 있다고 하면요, A.B에 대한 사이트를 만들고, 다시 그 결과를 .C의 수신자로 하는 사이트를 만들고, 다시 그 결과를 .D의 수신자로 하는 사이트를 만듭니다. 꽤나 현명하게 잘 만든거 같다는 생각이 들긴하는데요. 원래 컴파일러가 하는거랑 같은 방식이기도 하구요. 그런데 문제는 런타임의 구조상 ref형식으로 값을 리턴할 수 없다는 제약때문에 생깁니다. 물론 이런 제약은 CLR의 제약은 아닙니다. 다만 닷넷의 언어중에서 ref 리턴을 지원하는 언어가 없기 때문인데요. 그 말은 만약에 값형 변수에 대해서 연속적으로 '.'으로 연결된 표현식이 있다면, 대상이 되는 변수의 값은 박싱이 되면서 복사본이 생깁니다. 그리고 이후에 '.'로 연결된 것들은 그 복사본을 대상으로 연산이 수행된다는 것이죠. 예제를 보시면요,

public struct S
{
    public int i;
}

class D
{
    public S s;
    static void Main(string[] args)
    {
        dynamic d = new D();
        d.s = default(S);
        d.s.i = 10;
        Console.WriteLine(d.s.i);
    }
}


10이 결과로 찍힐거라고 예상할 수 있지만, 결과는 아래와 같습니다.


앞에서 설명드린대로, 'd.s.i = 10'에서 구조체 S가 박싱되면서 복사본이 생겼고, 그 복사본의 i에 10을 대입했기 때문에, 원래의 'd.s'의 i값에는 변화가 없는 것이죠. 이 문제는 참고하고 있는 Sam Ng의 2008년 12월 15일자 글에서 어떻게 할지 고민중이라고 적혀있었는데요. 출시된 VS2010에서 확인해본 결과 아무런변화가 없어서, 그대로 두기로 결정한 것으로 보입니다. 뭐 결국 핵심은 dynamic은 object랑 비슷하기 때문에 박싱이 일어난다는 점입니다.


- 상황3. 명시적으로 구현된 인터페이스의 메서드

우선 명시적으로 구현된 인터페이스의 메서드가 뭔지 부터 이야기를 해야 할 것 같습니다.



위 그림을 보면, S가 IEnumerable를 구현한다고 선언을 한 상태인데요. 인터페이스를 구현하는 방식에 'Implement Interface'와 'Implement Interface Explicitly'가 있는 걸 볼 수 있습니다. 전자가 우리가 흔히 인터페이스를 구현할 때 써온 암시적 인터페이스 구현이구요. 후자가 여기서 말씀드릴 명시적 인터페이스 구현입니다. 우선 위의 두 경우에 코드 모양이 어떻게 틀린지 확인해보도록 하지요.

public class S : IEnumerable
{
    public int i;

    #region IEnumerable Members

    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}

- 암시적 인터페이스 구현의 경우

public class S : IEnumerable
{
    public int i;

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }

    #endregion
}

- 명시적 인터페이스 구현의 경우

틀린 점을 발견하셨나요? 첫째로 명시적 인터페이스 구현의 경우, 메서드 이름인 GetEnumerator앞에 인터페이스의 이름이 '.'과 함께 붙어있습니다. 이 메서드가 어떤 인터페이스를 통해 구현된 건지 명시적으로 보여주는 것이죠. 그리고 한정자가 없으므로 private입니다. 그러므로 이 메서드는 인터페이스를 구현했지만 밖으로 노출이 되지 않습니다. 이렇게 명시적인 인터페이스 구현을 하는 경우에는 몇가지가 있을 수 있는데요. 이에 대한 더 자세한 설명은 유경상 수석님의 글을 참고하시면 매우 자세하게 아실 수 있습니다. 그럼 명시적 인터페이스 구현에 대한 설명은 여기까지로 하구요, 이게 dynamic과 무슨 관련이 있는지 알아보도록 하겠습니다. 아래와 같은 예제가 있다고 할때 말이죠,

public interface IFoo
{
    void M();
}

class C : IFoo
{
    #region IFoo Members

    void IFoo.M()
    {
        Console.WriteLine("C.M()!!");
    }

    #endregion
}

class D
{       
    static void Main(string[] args)
    {
        dynamic d = new C();

        d.M();
    }
}


예상으로는 "C.M()!!"이라는 메세지가 출력될 것 같기도 한데요. 그런데 앞서 말씀드렸던 명시적 인터페이스 구현의 특성 때문에, 런타임에 클래스C에서는 M이라는 이름의 메서드를 찾을수가 없다고 합니다. 그래서 런타임 바인더가 호출에 대해서 묶을 수 있는 IFoo라는 타입을 찾을 수 없다고 하네요. 이런 문제 때문에 위의 코드는 아래와 같은 에러를 내게 됩니다.


C라는 타입에 M의 정의가 없다는 에러메세지를 내면서 호출은 실패하게 됩니다.


- 마치면서

이제 비주얼 스튜디오 2010이 정식으로 출시되면서 C# 4.0에 대한 이야기도 현실과 매우 가까운 이야기가 되었습니다. 다만, 제 능력부족으로 글의 내용은 비현실적인거죠-_-;; 아무쪼록 도움이 되셨기를 바라면서 오늘은 여기서 끗~!


- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/12/15/dynamic-in-c-vi-what-dynamic-does-not-do.aspx
2. http://www.simpleisbest.net/archive/2008/06/23/2423.aspx

Welcome to Dynamic C#(15) - A/S for dynamic.

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

- 긴급 A/S 출동!

제가 글을 쓰면서 공부했던 내용들이 많이 바뀌었습니다-_- 그래서 저도 좀 뒤늦게 그 업데이트를 확인했구요~ 그래서 바뀐 내용에 대해서 A/S들어갑니다~~!


- 오버로드 판별 A/S!

아마 이 시리즈를 관심있게 읽어주신 분이라면!... 아흙 왜 또 눈물이... 아무튼 읽어주신 분이라면 유령메서드라는 걸 보셨을텐데요. 그게 너무 복잡하다보니, 유령메서드랑 관련한 내용은 하나도 채택되지 못했다고 합니다. 그리고 굉장히 간단한 규칙으로 정리를 했습니다. 만약에 어떤 메서드를 호출할 때 dynamic타입의 매개변수가 끼어있다면, 그 메서드에 대한 호출은 동적으로 디스패치된다는 것입니다. 그리고 런타임에서 dynamic타입의 매개변수의 실제값에 맞는 타입을 선택한다고 합니다.

class C
{
    static void M(dynamic d) { Console.WriteLine("dynamic"); }
    static void M(string s) { Console.WriteLine("string"); }
    static void M(int i) { Console.WriteLine("int"); }

    static void Main(string[] args)
    {
        dynamic d = "test";
        M(d);
    }
}


위와 같은 예제를 보면, M에 대한 호출은 런타임에 디스패치 됩니다. 그리고 어떤 M이 선택될지는 d의 실제값에 달여있는데요, 여기서는 d의 실제값이 string이기 때문에, 'M(string)'이 선택됩니다. 그러면, M(dynamic)은 어떻게 되는 걸까요? 바뀐 규칙에서 파라미터에서 dynamic타입이 있는 건, object타입이 있는것과 동일한 의미를 갖습니다. 즉 적합한 오버로드가 없을 때, 가장 나중에 선택이 되게 되겠죠.


- 대입형변환 A/S!

dynamic이 끼어있는 형변환 규칙도 약간의 변화가 있는데요, 그 규칙은 아래와 같습니다.

1. 모든 타입에서 dynamic타입으로 암시적 형변환이 가능하다. 기본적으로 object로 암시적 형변환이 가능한 타입이라면, dynamic으로도 암시적 형변환이 가능하다.

2. dynamic타입에서는 dynamic과 object를 제외한 다른 어떤 타입으로도 암시적 형변환이 불가능하다.

3. 하지만, 모든 '동적 표현식'은 다른 모든 타입으로 암시적 형변환이 가능하다.

4. 만약에 어떤 타입의 차이점이 dynamic과 object뿐이라면, 서로간에 암시적 형변환이 가능하다.

2, 3번에 대해서 좀 더 들여다 보면요, 타입의 형변환과 표현식의 형변환은 과연 뭐가 틀린걸까요? 일단 표현식의 형번환은 이때까지 '대입 형변환'이라고 불렀던 것을 가능하게 해줍니다. 예제를 하나 보면요.

string s = d;

이 형변환은 3번 규칙 때문에 성립합니다. 이 형변환은 동적 표현식에서 string으로의 형변환이지, dynamic타입에서 string으로의 형변환이 아니기 때문이죠. 이외에도 return, foreach, 프로퍼티에 값설정하기 등의 대부분의 경우는 이렇게 처리됩니다.

그렇다면 dynamic타입에서 string타입으로 형변환이 없어야 하는 이유는 뭘까요? covariance를 통해서 컴파일러가 이 형변환을 못하게 막는 걸 한번 확인해보겠습니다.

class C
{
    static void Main()
    {
        IEnumerable<dynamic> ied = null;
        IEnumerable<string> iei = null;
        var x = M(ied, iei);           
    }
    static T M<T>(IEnumerable<T> x, IEnumerable<T> y) { return default(T); } 
}

위 예제는 문제없이 컴파일 됩니다. 그리고 메서드의 타입유추 과정을 통해서 T를 dynamic이라고 골라내는 과정을 거칠텐데요, 그 과정은 아래와 같습니다.

1. 첫번째 매개변수는 IEnumerable<dynamic>이다. 그러므로 dynamic은 T의 후보가 된다.

2. 두번째 매개변수는 IEnumerable<string>이다. 그러므로 string은 T의 후보가 된다.

3. T의 후보군인 { dynamic, string }을 가지고 고민하는 과정에서 둘 사이의 관계를 보는데. string에서 dynamic으로의 형변환은 있지만, 그 반대는 성립하지 않는다. 그러므로 좀 더 일반적인 타입인 dynamic을 고르게 되는 것이다.

위 과정의 3번 단계에서 만약 dynamic에서 string으로 형변환이 가능했다면, 둘 중에 어떤 타입이 더 일반적인 타입인지를 결정할 수 없게 됩니다. 그렇다면 메서드 타입유추는 모호환 둘의 관계 때문에 실패하게 되겠죠. 그게 바로 dynamic에서 다른 타입으로의 형변환이 금지되어야 하는 이유입니다.

그리고 대입형변환이라는 용어는 아마 살아남지 못한 것 같습니다. C# 4.0 명세서에서 그 용어를 발견할 수 없었기 때문입니다. 그래도 혹시나 싶어서 Chris Burrows에게 질문을 남겨놨으니, 답이오면 반드시 알려드리겠습니다.


- 마치면서

앞서서 열심히 공부하고 적었던 내용이 바뀌고, 또 어떤 내용은 아예 통채로 날아가 버리니 아주 상쾌하네요~! 하하하-_- 그런데 바뀐 내용을 보니깐, 훨씬 간단하고 깔끔해지고, 더 이해하기 쉬워진 것 같아서 좋네요. 그럼 오늘은 여귀까쥐!


- 참고자료

1. http://blogs.msdn.com/cburrows/archive/2010/04/01/errata-dynamic-conversions-and-overload-resolution.aspx

Welcome to Dynamic C#(14) - 철지난 만우절에 낚여서 파닥파닥.

C# 2010. 4. 8. 09:00 Posted by 알 수 없는 사용자
- 뭔소리여

Eric Lippert가 만우절에 쓴 글에 아주 제대로 낚였습니다. C# 4.0이 정식으로 나오기 직전인데, 급하게 추가된 연산자가 있다더군요. 자바에 얼마전에 이런 기능이 추가되어서, C#의 우위를 유지하기 위해서 급하게 추가해서 가장 최근버전의 CTP버전에서 확인할 수 있다는 말이었습니다. 추가된 연산자는 '-->'랑 '<--'인데, 전자는  '~로 향해가는'이라는 의미이고, 후자는 '~로 접근하는'이라는 의미라고 합니다. 무슨 말인고 하니....

int x = 10;
// this is read "while x goes to zero"
while (x --> 0)
{
    Console.WriteLine("x = {0}", x);
}

위의 코드에서 while문안의 식은 x가 0에 도달할때까지라는 의미를 갖습니다.

int x = 10;
// this is read "while zero is approached by x"
while (0 <-- x)
{
    Console.WriteLine("x = {0}", x);
}

위의 코드에서는 0이 x에 의해서 접근되어질때까지(즉, x가 0으로 접근할 때까지)의 의미를 갖습니다.


제가 귀가 얇아서 일까요? 순간 '혹'해서, '좋은데? 낄낄낄'하고 생각하면서 글을 읽어나갈 무렵, 마지막 줄이 눈에 들어왔습니다. '만우절인데 얼레리꼴레리 속았지? 속았지? 우헤헤헤헤헤헤'. 저는 낚인줄도 모르고 신나게 파닥파닥 거린셈이죠. 호호호호호호. 글을 보니, C# QA팀의 테스터한명도 낚여서 파닥거렸다고 합니다. 그리고 참고로, '-->'이 연산자는 몇년 전부터 돌아다니는 쫌된 유머라고 하네요. 즉, 'x-- > 0'은 'x --> 0'과 동일하다는 거죠..... 아흙.

namespace Console1
{
 class Program
 {
  public static void Main(string[] args)
  {
   int x = 10;
   
   while(x --> 0)
   {
    Console.WriteLine("{0}",x);
   }
   
   Console.Read();
  }
 }
}

위의 코드는 아래와 같은 결과가 나오고, while문 안의 조건을 'x-- > 0'으로 바꿔도 결과는 동일합니다.




- 마치면서


사실 저도 이글을 가지고 여러분을 낚아볼려는 생각을 가지고 글을 쓰기 시작했는데, 그랬다가 많은 분들의 호응(?)을 받아서 교훈을 얻을까봐, 소개해드리는 정도로 마쳤습니다. 제가 참 좋아하는 블로거이고, 또 배우러 자주가는 블로그에서 낚일줄은 생각도 못했네요-_- 


- 참고자료

1. http://blogs.msdn.com/ericlippert/archive/2010/04/01/SomeLastMinuteFeatures.aspx

Welcome to Dynamic C#(13) - 아직도 가야할 길.

C# 2010. 1. 20. 09:00 Posted by 알 수 없는 사용자
- 제목이 표절인거 같은데...?

넵. 존경해 마지 않는 스캇 펙의 아직도 가야할 길을 요즘 감명깊게 읽고 있기 때문만은 아니구열. dynamic키워드로 아직도 써야 할 내용이 남아 있기 때문에, 한번 써봤습니당. 역시, 프로그래밍 언어의 현대적인 패러다임을 따라잡는 건, 단순히 사용하는 패턴만 익히는 게 아니라는 걸 다시한번 깨닫게 되네요. 그럼그럼~ 계속해서 한번 가보시져!


- 프로퍼티

d.Foo를 예로들면, d는 dynamic객체이고, Foo는 d속에 살고 있는 멤버 변수나 프로퍼티입니다. 컴파일러가 이런 구문을 만나면, 우선 Foo라는 이름을 payload속에다가 기록합니다. 그리고 런타임에게 d의 실제 타입을 찾아서 연결(바인드)해달라고 요청합니다.
payload : 캡슐화를 통해서 제공되는 컴퓨터 프로그램이나, 데이터 스트림속에서 사용자의 정보등을 나타내는 부분(출처 : http://en.wikipedia.org/wiki/Payload). 여기서는 C# 런타임 바인더가 해당 구문을 제대로 바인드하기 위해서 필요한 정보를 기록해 놓는 데이터 구조를 뜻합니당.

그리고 이런 프로퍼티는 항상 3가지경우 중 한가지경우에서 쓰이는데요. 값을 읽어오거나, 값을 대입하거나, 둘다 하거나(+=같이). 컴파일러가 사용된 모양을 보고, 어떻게 사용하려고 하는지도 payload에 같이 기록합니다. 즉, 읽기만 하는 경우에는 해당 프로퍼티는 읽기전용으로 기록을 하는 식으로 말이죠.

그리고 컴파일러는 이런 접근이 필드에 접근하는건지, 프로퍼티에 접근하는건지 딱히 구분하지 않습니다. 그건 나중에, 런타임이 구분을 하게됩니다. 그리고 컴파일할때, 이런 구문의 리턴 타입은 dynamic으로 설정됩니다.


- 인덱서

인덱서는 두가지로 생각해볼 수 있습니다. 첫번째는 매개변수가 있는 프로퍼티, 두번째는 배열이나 리스트같은 집합의 이름을 통한 메서드 호출. dynamic과 연관지어서 생각할때는 후자가 훨씬 도움이 됩니다. 메서드의 경우와 같이 인덱서도 정적으로 바운드 될 수 있지만, dynamic타입의 매개변수가 주어지고, 그 매개변수가 dynamic타입을 받는 인덱서로 정적 바운드가 되지 않는 경우, 오버로드 판별 과정에 유령이 끼어들게 됩니다. 그래서 인덱서의 수신자(receiver)는 정적타입이지만, 매개변수가 dynamic타입이라서 런타임에 늦은 바인딩이 일어나게 됩니다. 말로 설명하니깐, 깝깝하시죠? 실력부족으로 더 이상 말로는 깔끔하게 설명을 못드리겠네요-_-;; 예제로 설명을 드리면요.

public class C
{
    public int this[int i]
    {
        get
        {
            return i;
        }
    }

    public int this[dynamic d]
    {
        get
        {
            return d;
        }
    }
   
    static void Main(string[] args)
    {
        C c = new C();
        Console.WriteLine(c[5]);
        dynamic d = 7;
        Console.WriteLine(c[d]);
    }
}


위와 같은 코드를 보시면, C에 인덱서가 두개가 있습니다. 하나는 int를 매개변수로, 하나는 dynamic을 매개변수로 받죠. 그리고 Main메서드 안에서 하나는 int를, 하나는 dynamic타입의 매개변수를 넘겨주고 있습니다. 이 경우에 두번째 인덱서는 언제 어떻게 바인드될까요? 이경우는 비록 d가 타입이 dynamic이지만, 인덱서의 오버로드중에, 매개변수를 dynamic타입으로 받는 인덱서가 있습니다. 그래서 컴파일하는 시점에 "c[d]"이 인덱서 호출은 "public int this[dynamic d]"이 인덱서로 바인드 됩니다. 정적바인드가 되는거죠.

그런데, 만약에 dynamic을 받는 오버로드가 없다고 한다면 어떻게 될까요? 인덱서 호출을 받는 수신자는 c이고 c의 타입은 정적 타입인 C입니다. 하지만, 매개변수가 dynamic이죠. 그런데, dynamic과 일치하는 오버로드가 없습니다. 그래서 이때, 지지난 포스트에서 설명드렸던, 유령이 끼어들게 되는거죠.

메서드 처럼 생각하는게 편하다는 말씀은 드렸지만, 사실 프로퍼티와 유사한 면도 있습니다. 인덱서호출 역시 payload에다가 읽기, 쓰기등을 어떻게 하는지 기록합니다. 그래서 C# 런타임 바인더가 그 정보를 바탕으로 바인드할 수 있도록 말이죠. 그리고 인덱서의 리턴타입 역시 컴파일하는 시점에서는 dynamic으로 간주됩니다.


- 형변환

지지난 포스트에서 설명을 드릴때, dynamic은 다른 타입으로 암시적 형변환은 안되지만 되는 경우가 있다고 설명을 드렸었습니다. 그리고 지난 포스트에서 사실 그런 형변환이 대입 형변환이라는 설명도 드렸구요~. 형변환의 경우는 payload가 매우 단순해집니다. 왜냐면, 컴파일러는 이미 어떤 타입으로 형변환을 하려고 하는지 알고 있기 때문이죠. 그래서 컴파일러는 그냥 payload에 형변환 하려고 하는 타입을 기록하고, 런타임 바인더에게 가능한 모든 대입 형변환(형변환 연산자를 쓰는 경우에는 명시적 형변환도 같이)을 시도해보라고 이야기 해줍니다. 물론, dynamic타입이 아니라, 런타임에 결정될 실제 타입에서 목표 타입으로 시도해보겠죠.

형변환의 경우는 다른 모든 경우와 다르게 컴파일하는 시점에서 dynamic이 아닌 형변환의 목표타입을 리턴합니다. 위에서 말씀드렸듯이 이미 어떤 타입으로 형변환하려고 하는지 알 수 있게 때문이죠.


- 연산자

연산자는 초큼 특이합니다. 그냥 아무생각없이 훑어보면, 동적인 뭔가가 일어난다고 느끼기 힘들기 때문이죠. 그런데, d+1 같은 간단한 구문도 런타임에 바인드 되어야 합니다. 그 이유는 사용자정의 연산자가 끼어들 수 있기 때문입니다. 그래서, dynamic 매개변수를 갖는 모든 연산은 런타임에 바인드됩니다. +=나 -=같은 연산자도 포함해서 말이죠.

컴파일러는 연산자를 보면, d.Foo += 10 같이 멤버에 대입하는 연산이 있는지 혹은, d += 10 같이 변수에 대입하는 연산이 있는지 확인합니다. 그리고 그 과정에서 d를 ref를 통해 넘겨서 변경된 값이 유지되어야 하는지 확인합니다.

그리고 마지막으로 d.Foo += x 같은 구문이 있을 때, d.Foo가 바인드결과 delegate나 event타입이라면, 앞의 구문은 이벤트 수신자 추가 같은 적절한 메서드를 호출하도록 컴파일러가 연결해줍니다.


- 델리게이트 호출

데일게이트 호출은 메서드와 굉장히 유사합니다. 딱 한가지 틀린 점이 있다면, 호출되는 메서드의 이름이 명시되지 않는다는 것 뿐이죠. 그래서, 아래 예제의 두 호출은 모두 런타임에 바인드됩니다.

public class C
{
    static void Main(string[] args)
    {
        MyDel c = new MyDel();
        dynamic d = new MyDel();

        d();
        c(d);
    }
}


첫번째 호출은 매개변수가 없는 호출을 런타임에 바인드하게 됩니다. 런타임 바인더가 런타임에 호출의 수신자가 델리게이트 타입이 맞는지 확인하고 해당 델리게이트 시그니처와 일치하는 호출이 있는지 오버로드 판별을 통해서 찾게 됩니다.

두번째 호출은 매개변수가 dynamic타입이기 때문에, 런타임에 바인드됩니다. 컴파일러가 컴파일시점에서 c의 타입이 델리게이트라는 걸 확인할 수 있지만, 실제 오버로드 판별은 런타임에 가서 끝나게 됩니다.


- 마치면서

이제야 저는 dynamic에 대한 내용들이 머리속에서 아주 조금 자리를 잡은 듯한 느낌이네요. 저도 이런데 혼란스러웠던 지난 포스트를 보신 분들은 더 하시겠죠-_- 최대한! 최대한! 앞으로도 열심히 적겠습니다. 그럼 다음포스트에서 뵙죠~.

- 참고자료

1. http://blogs.msdn.com/samng/archive/2008/12/11/dynamic-in-c-v-indexers-operators-and-more.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가 아니더라도 기능을 활용할 일은 많은 거라는 생각이 드네요. 전 이래서 비주얼 스튜디오가 좋습니다. ㅋㅋㅋ....-_-;;;

TECH DAY 2009 행사 오픈!!!

VSTS 2010 팀 블로그 2009. 10. 22. 12:53 Posted by POWERUMC

한국 마이크로소프트에서 주최하는 TECH DAY 2009 행사가 오픈하였습니다. 지난 Dev Days 2008 에 이어 새롭게 개최하는 행사입니다. TECH DAY 는 온라인으로 진행되며 다양한 주제로 동영상 세미나가 준비되어 있습니다.

 

http://www.techdays.co.kr/

이번 TECH DAY 는 새로운 플랫폼 시대를 대비하기 위한 개발자와 IT 전문가를 위한 컨퍼런스 입니다. 특히 온라인 컨퍼런스의 장점이라면 시간과 장소에 구애 받지 않고 언제나 오픈되어 있는 컨퍼런스라는 점입니다.

트랙은 키노트와 개발 트랙, IT 전문가 트랙으로 구성되어 있으며, 특히 개발 트랙과 IT 전문가 트랙은 굉장히 많은 동영상 콘텐트가 준비되어 있습니다. 그리고 개발 플랫폼 트랙은 제가 몸담고 있는 VSTS 2010 팀에서 전체 세션을 맡았습니다.    

 

이번 TECH DAY 2009 행사에 많은 참여를 부탁드립니다. 경품도 많이 준비가 되어있으니 경품 이벤트에도 도전해 보세요.

- 이번엔 웹디자이너와 배포에 대해서

부족한 번역이지만, 이번엔 웹 디자이너와 배포에 대해서 적어볼까 합니다.


- Visual Studio 2010 Web Designer Improvement

 

Visual Studio 2010의 웹페이지 디자이너는 더 뛰어난 CSS 호환성, HTML ASP.NET 마크업의 코드조각에 대한 추가적인 지원과 Jscript를 위해서 다시 디자인된 인텔리센스등으로 더 향상된 기능을 제공합니다.

 

 

- Improved CSS Compatibility

 

Visual Studio 2010 Visual Web Developer 디자이너는 CSS 2.1 표준과 호환되는데요. 이 디자이너는 HTML 소스와의 통합성을 높이면서 기존의 버전의 Visual Studio보다 전체적으로 더 견고한 기능을 제공합니다. 내부적으로는 앞으로 추가될 렌더링, 레이아웃, 사용성 및 유지보수를 위한 설계적인 측면에서의 향상도 있었습니다.

 

 

- HTML and Jscript Snippets

 

HTML에디터는 인텔리센스를 이용해서 태크이름을 자동완성합니다. 그리고 인텔리센스의 코드조각 기능이 전체 태그와 기타 부분을 완성합니다. VisualStudio 2010에서는 인텔리센스의 코드조각 가능이 C# Visual Basic에 적용되었던 것 처럼 Jscript에 대해서도 지원될 것입니다.

 

VisualStudio 2010에는 200개 이상의 코드조각이 ASP.NET HTML의 일반적인 태그와 필요한 속성(runat=”server” 같은)과 특정 태그들에 공통적인 속성들(ID, DataSourceID, ControlToValidate, Text같은)의 자동완성을 도와줍니다.

 

추가적인 코드조각들을 다운받을 수 있으며, 팀의 공동작업이나 개인작업에 공통으로 쓰이는 마크업의 블록을 코드조각으로 뽑아내서 사용할 수도 있습니다.

 

 

- Jscript IntelliSense Enhancements

 

VisualStudio 2010에선 더 풍부한 편집을 위해서 Jscript의 인텔리센스가 다시 디자인 되었습니다. 이젠 인텔리센스가 registerNamespace같은 메서드나 기타 비슷한 자바스크립트 프레임워크의 기술을 이용해서 동적으로 생성되는 객체를 인식할 수 있습니다. 그리고 방대한 스크립트 라이브러리를 분석하고 화면에 인텔리센스를 표시하는데 걸리는 시간을 사용자가 거의 못 느낄 정도로 향상시켰습니다. 호환성 역시 대단히 향상되어서 외부 라이브러리나 다양한 코딩 스타일도 지원할 수 있게 되었습니다. 문서화를 위한 주석은 타이핑하는 즉시 파싱되어서 인텔리센스에서 바로 활용할 수 있습니다.

 

 

- Web Application Deployment with Visual Studio 2010

 

현재 웹 어플리케이션을 배포하는 작업은 우리가 꿈꾸었던 것만큼 쉽지 않았죠. ASP.NET 개발자는 종종 아래와 같은 문제에 직면하곤 합니다.

 

  FTP같은 기술을 사용해야 하는 공동 호스팅 사이트에 배포하는 경우. 게다가 데이터베이스를 세팅하기 위한 SQL스크립트를 직접 실행해야 하거나 가상 디렉토리같은 IIS세팅도 바꿔야 한다.

  엔터프라이즈 환경에서는 웹 어플리케이션을 배포하는 것으로 끝나지 않고, ASP.NET 설정파일이나 IIS세팅도 수정해야 한다. 데이터베이스 관리자는 데이터베이스를 돌리기 위해 필요한 SQL스크립트를 돌려야 한다. 그런 설치작업은 종종 몇 시간을 잡아먹으며 노동에 가까운 일이 되며, 세세하게 문서화 되어야 한다.

 

VisualStudio 2010은 웹 어플리케이션을 중단없이 배포할 수 있게 도와주는 새로운 기술을 통해서 이런문제들을 해결합니다. 이런 기술들중의 하나가 IIS Web Deployment Tool(MsDeploy.exe)이죠.

 

VisualStudio 2010에서 웹배포에 관련된 요소들은 아래와 같이 크게 분류할 수 있습니다.

 

  Web packaging

  Web.config Transformation

  Database deployment

One-Click Publish for Web applications

 

아래의 섹션들에서 하나씩 자세하게 설명을 해보겠습니다.

 

 

Web Packaging

 

VisualStudio 2010에서는 MSDeploy 툴을 사용해서 어플리케이션을 압축해서 압축파일(.zip)을 생성하는데, 그 파일을 웹 패키지라고 부릅니다. 패키지파일은 어플리케이션에 대한 메타데이터와 아래의 내용들을 포함합니다.

 

 어플리케이션 풀 세팅과 에러페이지 세팅, 등등을 포함하는 IIS세팅

  웹페이지와 사용자 정의 컨트롤, 정적인 컨텐츠(이미지와 HTML파일), 등등을 포함하는 실제 웹 컨텐츠

  SQL 서버의 데이터베이스 스카마와 데이터

  보안 인증서, GAC에 설치할 컴포넌트, 레지스트리 세팅, 등등

 

웹 패키지는 아무 서버에나 복사할 수 있고, IIS 매니저를 사용해서 수동으로 설치할 수 있습니다. 또는, 커맨드라인 명령어나 배포API를 사용해서 자동으로 배포할 수도 있습니다.

 

VS2010에서는 웹패키지를 만들기 위해서 사용가능한 MSBuild task target을 제공합니다. 더 많은 정보는 Vishal Joshi의 블로그에 있는 10 + 20 reasons why you should create a Web Package를 참조하시길 바랍니다.

 

 

Web.config Transformation

 

웹 어플리케이션의 배포를 위해서 VisualStudio 2010에서는 XML Document Transform (XDT)가 도입되었는데, 이 요소는 개발설정에서 Web.config를 읽어와서 제품설정(production setting)으로 변환하게 도와줍니다. 변환에 관련된 세팅은 변환파일인 web.debug.config, web.release.config, 그리고 기타파일(이 파일들의 이름은 MSBuild 설정과 매치된다)등에 명시되어 있습니다. 변환파일에는 Web.config파일을 배포하기 위해 필요한 수정사항들만 포함되어 있습니다. 이런 수정사항들을 간단한 문법으로 명시해주면 되는거죠.

 

아래의 예제는 배포의 릴리즈설정에 의해 생성가능한 web.release.config파일의 일부분인데요. 예제의 내용중에서 Replace키워드를 보면, 배포과정에서 Web.config파일의 connectionString 노드의 값이 어떻게 바뀌어야 하는지 명시해주고 있습니다.

 

<connectionStrings xdt:Transform="Replace">

  <add name="BlogDB" connectionString="connection string detail]" />

</connectionStrings>

 

더 자세한 정보는, Vishal Joshi의 블로그의 Web Deployment: Web.Config Transformation를 보시면 됩니다.

 

 

- Database Deployment

 

VisualStudio 2010의 배포 패키지에는 SQL 서버 데이터베이스에 대한 의존성 역시 포함될 수 있습니다. 패키지 정의의 일부분으로 원본 데이터베이스의 연결문자열을 명시해줄 수 있습니다. 웹 패키지를 만들때 VisualStudio2010에 의해서 데이터베이스 스키마와 선택적으로 데이터에 대해서 SQL 스크립트가 만들어지고 패키지에 포함됩니다. 사용자 정의 SQL 스크립트를 만들어서 서버에서 순차적으로 실행되어야 할 순서를 지정해줄 수도 있습니다. 배포할때, 타겟 서버에 대한 연결문자열을 제공하고, 배포 프로세스에서 이 연결문자열을 이용해서 해당 데이터베이스에 SQL 스크립트를 실행해서 데이터베이스 스키마를 만들고 데이터를 추가합니다.

 

추가적으로, One-Click Publish를 이용하면 어플리케이션이 원격의 공용 호스트에 게시된 후에 데이터베이스를 직접적으로 게시할 수 있도록 배포를 설정할 수 있습니다. 자세한 내용은 Vishal Joshi의 블로그의 Database Deployment with VS 2010를 참조하시면 됩니다.

 

 

- One-Click Publish for Web Application

 

VisualStudio 2010에서는 IIS 원격 관리 서비스를 이용해서 원격의 서버에 웹 어플리케이션을 게시할 수 있도록 도와줍니다. 게시를 위해서 호스팅 계정이나 테스트 서버나 스테이징 서버를 위한 게시 프로파일(publish profile)을 생성할 수 있습니다. 각각의 프로파일은 안전하게 계정정보를 저장할 수 있구요. 그러면 Web One Click Publish 툴바를 이용해서 한번의 클릭으로 타겟서버에 배포할 수 있게 됩니다. VisualStudio2010에서는 MSBuild 커맨드 라인을 통해서도 배포할 수 있는데요. 이 기능을 통해서 지속적인 통합 모델을 이용하는 팀 빌드 환경에 게시작업을 포함할 수 있습니다.

 

더 자세한 정보는, Vishal Joshi의 블로그의 Web 1-Click Publish with VS 2010를 보시면 되구요, VisualStudio 2010에서의 웹 어플리케이션 배포에 대한 비디오 영상을 보려면, Vishal Joshi의 블로그의  VS 2010 for Web Developer Previews를 보시면 됩니다.

 

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/

- 이어서 이어서!

지난 시간에 이어서 Dynamic Data에 관련된 기능들을 보시겠습니다.



- Entity Templates

 

Entity Templates를 이용하면 사용자 정의 페이지를 만들지 않고도 데이터의 레이아웃을 사용자가 편집할 수 있습니다. 페이지 템플릿은 FormView 컨트롤(기존 버전의 Dynamic Data의 페이지 템플릿에서 사용하던 DetailView컨트롤 대신에) DynamicEntity 컨트롤을 사용해서 Entity templates를 렌더링합니다. 이렇게 하면 사용자는 Dynamica Data가 렌더링한 마크업에 대해서 더 많은 제어를 할 수 있게 되죠.

 

아래의 목록은 Entity templates를 포함하고 있는 새로운 프로젝트 폴더의 구조입니다.

 

\DynamicData\EntityTemplates

\DynamicData\EntityTemplates\Default.ascx

\DynamicData\EntityTemplates\Default_Edit.ascx

\DynamicData\EntityTemplates\Default_Insert.ascx

 

EntityTemplates폴더는 모델 객체들을 어떻게 화면에 표시할지에 대한 템플릿을 담고 있습니다. 기본적으로 객체들은 ASP.NET 3.5 SP1 Dynamic Data에서 생성된 DetailsView의 마크업과 유사한 모양의 마크업을 생성해주는 Default.ascx를 이용해서 렌더링되는데요. 다음의 예제는 Default.ascx 컨트롤의 마크업 입니다.

 

<asp:EntityTemplate runat="server" ID="TemplateContainer1">

  <ItemTemplate>

    <tr

      <td>

        <asp:Label ID="Label1" runat="server" OnInit="Label_Init" />

      </td>

      <td>

        <asp:DynamicControl runat="server" OnInit="DynamicControl_Init" />

      </td>

    </tr>

  </ItemTemplate>

</asp:EntityTemplate>

 

기본 템플릿은 전체 사이트의 디자인과 느낌을 바꾸기 위해서 수정가능합니다. 기본 템플릿에는 디스플레이, 수정, 그리고 삽입을 위한 템플릿이 포함되어 있습니다. 새로 추가하는 템플릿들은 데이터객체의 이름을 기반으로 해서 추가될 수도 있는데, 그 타입의 객체의 디자인과 느낌을 수정하고 싶을때가 그렇습니다. 예를들면, 아래와 같은 템플릿을 추가할 수 있습니다.

 

\DynamicData\EntityTemplates\Products.aspx

이 템플릿은 아래와 같은 마크업을 포함하고 있겠죠.

 

<tr>

  <td>Name</td>

  <td><asp:DynamicControl runat="server" DataField="ProductName" /></td>

  <td>Category</td>

  <td><asp:DynamicControl runat="server" DataField="Category" /></td>

</tr>

 

새로운 Entity templates DynamicEntity 컨트롤을 사용하는 페이지에서 화면에 나타납니다. 런타임에 이 컨트롤은 Entity template의 내용으로 대체되는데요. 아래의 마크업은 Entity template를 사용하는 Detail.aspx 페이지 템플릿의 FormView컨트롤입니다. 마크업의 DynamicEntity 요소을 주목해서 보시죠.

 

<asp:FormView runat="server" ID="FormView1"

    DataSourceID="DetailsDataSource"

    OnItemDeleted="FormView1_ItemDeleted">

  <ItemTemplate>

    <table class="DDDetailsTable" cellpadding="6">

      <asp:DynamicEntity runat="server" />

      <tr class="td">

        <td colspan="2">

          <asp:DynamicHyperLink ID="EditHyperLink" runat="server"

              Action="Edit" Text="Edit" />

          <asp:LinkButton ID="DeleteLinkButton" runat="server"

              CommandName="Delete"

              CausesValidation="false"

              OnClientClick='return confirm("Are you sure you want to delete this item?");'

              Text="Delete" />

        </td>

      </tr>

    </table>

  </ItemTemplate>

</asp:FormView>

 

 

- New Field Templates for URLs and E-mail Addresses

 

ASP.NET 4 에서는 새로운 내장 필드 템플릿인 EmailAddress.ascx, Url.ascx가 추가되었습니다. 이 템플릿들은 DataType 속성과 함께 EmailAddress Url로 선언된 필드에 사용됩니다. EmailAddress타입의 객체의 경우에는 필드가 ‘mailto:protocol’를 사용해서 만든 하이퍼링크로 표시되구요, 사용자가 이걸 클릭하면 사용자의 이메일 클라이언트 프로그램을 실행하고 기본메세지를 생성해줍니다. Url타입의 객체는 그냥 일반적인 하이퍼링크로 표시되구요.

 

아래의 예제는 필드를 마크하는 방법을 보여줍니다.

 

[DataType(DataType.EmailAddress)]

public object HomeEmail { get; set; }

 

[DataType(DataType.Url)]

public object Website { get; set; }

  

- Creating Links with the DynamicHyperLink Control

 

Dynamic Data는 사용자가 웹사이트가 접근했을 때 보여지는 URL을 컨트롤하기 위해서 .NET 프레임워크 3.5 SP1에 추가되었던 라우팅 기능을 사용합니다. DynamicHyperLink 컨트롤이 Dynamic Data를 사용한 사이트에 접근하는 링크를 쉽게 만들 수 있도록 도와줍니다. 아래의 예제는 DynamicHyperLink컨트롤을 사용하는 예제입니다.

 

<asp:DynamicHyperLink ID="ListHyperLink" runat="server"

    Action="List" TableName="Products">

  Show all products

</asp:DynamicHyperLink>

 

이 마크업은 Global.asax파일에 정의된 규칙대로 라우팅되는 링크를 만들고, 그 링크는 Products테이블의 List페이지를 가리킵니다. DynamicHyperLink컨트롤은 컨트롤이 위치한 Dynamic Data 페이지에 기반해서 자동으로 테이블의 이름을 명시해줍니다.

 

 

- Support for Inheritance in the Data Model

 

현재 Entity Framework LINQ to SQL 둘다 모두 데이터 모델상에서의 상속을 지원합니다. 예를 들면, InsurancePolicy 테이블이 있는 데이터베이스를 생각해볼 수 있는데요. 추가로 InsurancePolicy와 동일한 필드를 가지고 몇가지 추가적인 필드만 가지는 CarPolicy HousePolicy테이블도 가질 수 있습니다. Dynamic Data는 이렇게 데이터 모델에서 상속관계를 갖는 객체에 대해서 scaffolding을 할 수 있도록 개선되었습니다.

 

 

- Support for Many-to-Many Relationships(Entity Framework Only)

 

엔티티 프레임워크는 다대다 관계를 가지는 테이블을 풍부한 기능으로 지원하고 있고, 관계를 엔티티 객체들간의 컬렉션으로 노출하는 형태로 구현이 되어있습니다. ManyToMany.ascx ManyToMany_Edit.ascx 필드 템플릿은 다대다 관계를 갖는 데이터를 표시하고 수정하는 걸 지원하기 위해서 추가되었습니다.

 

 

- New Attributes to Control Display and Support Enumerations

 

DisplayAttribute를 통해서 필드가 어떻게 화면에 표시될지를 추가적으로 컨트롤 할 수 있습니다.기존 Dynamic Data버전에 있던 DisplayName 속성은 필드의 캡션으로 쓰일 이름을 변경할 수 있도록 지원했었는데요. 새로운 DisplayAttribute 클래스는 필드를 표시하는데 있어서 필드의 데이터를 어떻게 정렬할 것인지, 필드에 필터를 적용할 것인지 같은 옵션을 제공합니다. 그리고 이 속성은 추가적으로 GridView컨트롤의 레이블, DetailsView컨트롤의 이름, 필드의 도움말, 필드의 워터마크(필드가 텍스트입력을 받는다면)등에 대한 독립적인 제어를 제공합니다.

 

EnumDataTypeAttribute 클래스는 필드를 열거형데이터 타입에 연결해주기 위해서 추가되었습니다. 이 속성을 필드에 주게되면, 열거형 타입을 명시하게 되는데요. Dynamic Data에서는 열거형 값을 표시하고 수정하는데 Enumeration.ascx를 사용합니다. 이 템플릿은 데이터의 값을 열거형의 이름과 연결합니다.

 

 

- Enhanced Support for Filters

 

Dynamic Data 1.0boolean열과 외래키열에 대한 내장필터를 갖고 릴리즈 되었었습니다. 하지만,이 필터들은 열을 화면에 표시할지, 열의 데이터를 어떤 순서로 출력할지 명시해줄 수는 없었는데요. 새로운 DisplayAttribute 속성은 이 문제를 화면에 열을 필터로 표시할지 그리고 어떤 순서로 표시할지 설정할 수 있게 함으로써 해결해줍니다.

추가적으로 향상된 부분은 필터링을 지원하는 부분이 웹폼의 QueryExtender를 이용하도록 재작성됐다는 점입니다. 이로써 필터가 사용될 데이터 소스 컨트롤을 몰라도 필터를 작성할 수 있습니다. 이런 확장과 더불어서 이젠 필터도 템플릿 컨트롤로 작성되게 되어서 새로운 필터를 작성해서 추가할 수 있게 되었습니다. 마지막으로, DisplayAttribute 클래스는 UIHint가 기본적인 필드템플릿을 오버라이드할 수 있게 지원하는 것 처럼, 기본적인 필터를 오버라이드할 수 있도록 지원합니다.



- 마치면서

다음번에는 웹 디자이너와 배포에 어떤 새로운 기능들이 추가되고 개선되었는지에 대해서 말씀드리겠습니다!

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#(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/

VSTS 2010 팀 맴버 추가 모집

VSTS 2010 팀 블로그 2009. 7. 27. 09:33 Posted by POWERUMC

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

저희 VSTS 2010 팀은 이번 분기를 기점으로 보다 세분화하고 다양한 활동을 하기 위해 팀 맴버를 추가로 모집하고 있습니다.


   

활동 영역

온라인 활동 영역

팀 블로그 활동

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

온라인 세미나

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

온라인 커뮤니티(예정)

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

   

오프라인 활동 영역

오프라인 스터디

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

오프라인 세미나

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

기고

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

책 집필, 번역(예정)

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

Microsoft MVP 추천

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

   

모집 분야

  • Cloud Development
  • Parallel Development
  • Web Development
  • Windows 7 Development
  • RIA Development
  • Architect Development
  • Office Business Application Development
  • .NET Framework 4.0
  • Visual Studio 2010
  • Visual Studio Team System 2010
  • ETC…

   

마감

이번 분기는 단순한 스터디를 뛰어 넘는 다양한 활동을 계획하고 있습니다. 그러므로 특별히 마감 일자는 없습니다.

조만간 저희 팀에서 워크샵을 갈 예정인데, 그 전에 함께 워크샵을 가면 좋겠네요.

   

지원 방법

아래의 양식을 채워주시고 umc골뱅이dotnetxpert.com 으로 보내주세요.

이름

  

나이

  

블로그

  

전화번호

  

티스토리 아이디

  

소개

(직업 및 회사명 포함)

관심 분야

(중복 가능)

   

   

지원 시 유의 사항

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

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

지난 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