'.Net'에 해당되는 글 13건

  1. 2011/01/30 MSDN Magazine 2010/12월호
  2. 2009/02/12 뮤텍스를 사용한 다중실행 방지(C#)
  3. 2008/10/25 六曜を取得する
  4. 2008/10/22 한방에 예외로그를 처리하기.
  5. 2008/09/30 ClickOnce로 배포된 어플리케이션 버젼 취득. ClickOnce配布アプリのバージョン取得
  6. 2008/05/20 OTP솔루션 구축방법
  7. 2007/12/10 진정한 웹다중Tier개발의 시작? VOLTA플랫폼
  8. 2007/10/06 MS,.Net의 소스코드 공개
  9. 2007/06/12 WindowsClient.net 오픈
  10. 2007/05/10 LINQ의 발전과 C# 설계에 미치는 영향
2011/01/30 15:54

MSDN Magazine 2010/12월호

http://msdn.microsoft.com/ko-kr/magazine/default.aspx

언제나 다 중요한 내용들이지만 시대를 뒤쳐지지 않기위해..
꼭 봐둘만한 기사

저작자 표시 비영리 변경 금지
Trackback 0 Comment 0
2009/02/12 22:44

뮤텍스를 사용한 다중실행 방지(C#)

많이 쓰이는건데..
WinApp의 다중실행을 방지하는 샘플입니다.
Program.cs를 아래와 같이 변경해주세요.


    static class Program
    {
        // 어플리케이션의 이름
        private static string strAppConstName = "적당한 어플리케이션의 이름";

        // 다중기동을 방지하는 뮤텍스인스턴스
        private static Mutex mutexObject;

        [STAThread]
        static void Main()
        {
            // Windows 2000(NT 5.0)이후만 글로벌 뮤텍스가 사용가능
            OperatingSystem os = Environment.OSVersion;
            if ((os.Platform == PlatformID.Win32NT) && (os.Version.Major >= 5))
            {
                strAppConstName = @"Global\" + strAppConstName;
            }

            try
            {
                // 뮤텍스를 생성
                mutexObject = new Mutex(false, strAppConstName);
            }
            catch (ApplicationException e)
            {
                // 글로벌 뮤텍스에 의한 다중실행 방지
                MessageBox.Show("이미 실행되고 있습니다.", "다중실행방지");
                return;
            }

            // 뮤텍스를 취득
            if (mutexObject.WaitOne(3000, false))
            {
                //실행
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new frm_SignIn());

                //프로그램사용이 끝났으니 뮤텍스를 해방
                mutexObject.ReleaseMutex();
            }
            else
            {               
                //이미 실행중이니 경고 메시지
                MessageBox.Show("이미 실행되고 있습니다.", "다중실행방지");
            }

            // 뮤텍스를 파기하고 완전종료
            mutexObject.Close();

        }
    }

Trackback 0 Comment 0
2008/10/25 21:52

六曜を取得する

        /// <summary>
        /// 六曜を取得する。
        /// </summary>
        /// <param name="targetdate">調査する日付</param>
        /// <returns></returns>
        internal static string GetRokuyouString(DateTime targetdate)
        {
            int month;
            int uru;
            int day;
            string rtn = "";

            System.Globalization.JapaneseLunisolarCalendar jpnc = new System.Globalization.JapaneseLunisolarCalendar();

            month = jpnc.GetMonth(targetdate);
            day = jpnc.GetDayOfMonth(targetdate);

            //閏月を取得
            uru = jpnc.GetLeapMonth(jpnc.GetYear(targetdate), jpnc.GetEra(targetdate));

            if ((uru > 0) && (month - uru >= 0)) //閏月がある場合の補正
            {
                month = month - 1;
            }


            switch ((month + day) % 6)
            {
                case 0:
                    rtn = "大安";
                    break;

                case 1:
                    rtn = "赤口";
                    break;

                case 2:
                    rtn = "先勝";
                    break;

                case 3:
                    rtn = "友引";
                    break;

                case 4:
                    rtn = "先負";
                    break;

                case 5:
                    rtn = "仏滅";
                    break;

                default:
                    break;
            }

            return rtn;
        }

Trackback 0 Comment 0
2008/10/22 09:41

한방에 예외로그를 처리하기.

어플리케이션에서 발생하는 모든 예외를 한곳에서 로그처리를 하면 굉장히 편리한 경우가 많습니다.
개발자가 많아지거나 규모가 커지면 커질수록 예외처리를 하기가 쉽지않은데요..
아래의 코드를 이용해서 처리가 안된 예외를 자동으로 캐치해서 로그를 남겨줄수있습니다.
이렇게하면 알수없는 에러가 발생했을경우 로그가 남기때문에 수정하기가 쉬워지죠...

try
{
    //여기서 예외가 발생안하면 웹어플리케이션
    //ここで例外が発生しなかったらウェブアプリケーション
    System.Web.HttpContext.Current.ApplicationInstance.Error += new EventHandler(ApplicationInstance_Error);
}
catch (Exception)
{
    //윈도우 어플리케이션의 UnhandledException이벤트에 핸들러를 추가
    //例外が発生したらウィンドウアプリケーション
    System.AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}

static void ApplicationInstance_Error(object sender, EventArgs e)
{
    //로그를 남기는 처리를 집어넣기
    //WriteLog("Exception発生!!!",System.Web.HttpContext.Current.Error.ToString(), LogLevel.Fatal);
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    //로그를 남기는 처리를 집어넣기
    //WriteLog("Exception発生!!!", e.ExceptionObject.ToString(), LogLevel.Fatal);
}


위의 코드로 처리되지않은 에러들이 각각 ApplicationInstance_Error와 CurrentDomain_UnhandledException 이벤트핸들러로 넘어오게됩니다.
그럼 필요한 코드를 집어넣어서 파일 또는 이벤트로그에 로그를 남기시면 에러가 발생했을경우 유저를 붙잡고 어떤 에러가 낳는지 캐물을 필요도 없게됩니다...ㅎ

저는 위의 코드를 별도로 개발한 범용로그컴퍼넌트에 붙여넣어서 Logger가 로딩되면 자동으로 어플리케이션에 붙히도록 해두었습니다.


Trackback 0 Comment 0
2008/09/30 20:42

ClickOnce로 배포된 어플리케이션 버젼 취득. ClickOnce配布アプリのバージョン取得

try
{
    //ClickOnce의 버젼 취득
    //ClickOnceのバージョン取得
    return System.Deployment.Application.ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();
}
catch (System.Deployment.Application.DeploymentException ex)
{
    //ClickOnce배포가 아니므로 어셈블리버젼을 취득
    //ClickOnce配布ではないのでアセンブリのバージョンを取得
    return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
catch (Exception ex)
{
    return "확인불가";
}
Trackback 0 Comment 0
2008/05/20 14:31

OTP솔루션 구축방법

MSDN기사입니다.
최근 패스워드보안솔루션의 주류가 되어가고 있는 원타임패스워드 솔루션의 구축방법입니다.

세큐리티 솔루션은 접할 기회가 많지 않으니 도움이 될만한 자료인듯..

http://msdn.microsoft.com/ko-kr/magazine/cc507635.aspx
Trackback 0 Comment 0
2007/12/10 11:27

진정한 웹다중Tier개발의 시작? VOLTA플랫폼

사용자 삽입 이미지






플랫폼이라고 해야할지 기술이라고 해야할지 잘모르겠지만
 (클라이언트입장에서는 플랫폼, 서버입장에서는 기술?이랄까)
MS에서 새로운 아이디어를 내놓았습니다.

일반적으로 M-Tier라고 하면 데이터-> 서버 -> 클라이언트의 3개이상의 물리적 플랫폼에서
데이터로직, 비지니스로직, 프레젠테이션로직을 구현하는것을 말하는데
진정한 M-Tier를 구현하기위해서는 클라이언트 어플리케이션을 독립실행하는 형태로 개발해야하죠.

최근에는 워낙 웹이 발달하게되어서 Javascript라든지 스마트클라이언트라든지 Ajax를 최대한 활용하는 방식으로 M-Tier의 프레젠테이션계층을 개발하는것이 주류가 되었죠.

하지만 웹브라우저에서 Rich한 UI를 개발하기 위해서는 굉장히 많은 노력이 필요합니다.
그 노력의 대부분이 별거아닌 UI를 제어하기위해 많은 양의 Javascript를 사용해야 하거나
비지니스로직계층과의 통신을 위해서 낭비하게 되죠.

원인은 웹브라우저상에서는 실질적으로 자바스크립트만이 유일한 프로그래밍 방법이죠.
다른 모든 계층은 스크립트언어가 아닌 프로그래밍언어를 사용하는데 말이죠..
자바스크립트가 나쁘다는게 아니라 개발시에 많은 오버헤드를 발생시킵니다.
설계,디버깅, 테스트등 많은 단계에서요..

또한 중요한것은 어떤 로직을 서버에두고 어떤 로직을 클라이언트에 둘까 하는 문제점입니다.
MS에서 Volta라는 새로운 개발툴을 내놓았습니다. MSIL로 작성된 DLL을 웹브라우저에서 로딩하여 자바스크립트를 대신하여 브라우저 제어할수 있는 기술입니다.(DLL을 랩핑해서 자바스크립트로 변환하는건가????)
좀더 거시적으로 말하자면 프레젠테이션층의 코드를 친숙한 C#이나 VB에서 개발한후에 동적으로 서버에서 실행할지 클라이언트에서 실행할지를 결정지을수 있죠.

기사와 공식사이트의 URL을 올려놓으니 흥미있으신분들은 방문해보시길...

기사 - >http://www.zdnet.co.kr/news/enterprise/dev/0,39031103,39164019,00.htm

공식사이트 - > http://labs.live.com/volta/


개인적인 감상으로는 아이디어는 좋은데...새로운 패러다임이라고는 할수 없음...기존의 기술의 극대활용이랄까...
차라리 XAML이 더 신선한 듯한....
근본적인 문제는 자바스크립트를 쓰려고 하기때문이란 말이다...-.-
Trackback 0 Comment 0
2007/10/06 16:37

MS,.Net의 소스코드 공개

MS가 개발자들의 개발편의성과 .Net의 내부구조및 동작의 이해를 돕기위해서 .Net의 소스코드를 올해안에 공개한다고 합니다.
일단 공개되는 애들은 아래와 같고....내년에는 더많은 부분을 공개할 예정이라는...
System,System.IO,
System.CollectionsSystem.Configuration,
System.Threading,
System.NET,
System.Security,
System.Runtime,System.Text등,
ADO.Net(System.Data),
ASP.NET(System.Web),
Windows Forms(System.Windows.Forms),
WPF(System.Windows)

편리한 기능은 VS2008에서 .Net 라이브러리 소스에 스텝인해서 들어갈수도 있다더군요.
기대됩니다만 수정 및 재배포는 금지된다고 하네요.

--참고 기사--
Releasing the Source Code for the .NET Framework Libraries
http://weblogs.asp.net/scottgu/archive/2007/10/03/releasing-the-source-code-for-the-net-framework-libraries.aspx

MS、.NETのソースコードを公開へ
http://www.atmarkit.co.jp/news/200710/05/dotnet.html
Trackback 0 Comment 0
2007/06/12 13:29

WindowsClient.net 오픈

윈도우 클라이언트 개발자들의 커뮤니티가 저번달?에 오픈했습니다.
asp.net개발자들의 커뮤니티인 http://asp.net 과 자매사이트입니다.

http://windowsclient.net/


Trackback 0 Comment 0
2007/05/10 16:30

LINQ의 발전과 C# 설계에 미치는 영향

MSDN 매거진에 기고된 내용을 뽑아옵니다.

작년에 잠깐 내용을 들었던 내용인데 상당히 기대되는 기술입니다.
나온다면 O/R Mapper는 필요없게될듯...ㅎ


http://msdn.microsoft.com/msdnmag/issues/07/06/CSharp30/Default.aspx?loc=ko -> 원본URL
http://msdn.microsoft.com/msdnmag/issues/07/06/?loc=ko -> MSDN Magazine 6월호






이 기사는 Visual Studio 시험판 버전인 코드 이름 "Orcas"를 기준으로 합니다. 여기에 포함된 모든 정보는 변경될 수 있습니다.

이 기사에서 다루는 내용:
  • C#과 LINQ
  • LINQ의 발전
  • 코드에서 SQL 쿼리
이 기사에서 사용하는 기술:
LINQ, C#


목차


필자는 James Burke의 진행으로 디스커버리 채널에서 방영한 Connections 시리즈의 열렬한 팬이었습니다. 이 시리즈의 기본적인 전제는 전혀 관계가 없을 것 같은 발견이 다른 발견에 영향을 주어 궁극적으로 현대의 편리한 일상으로 이어졌다는 것입니다. 여기에서 얻을 수 있는 교훈은 어떤 발전도 고립된 상태로 이루어지지 않는다는 것입니다. 물론 이러한 명제는 LINQ(Language-Integrated Query)에도 적용됩니다.

간단히 말해 LINQ는 형식이 안전한(type-safe) 방식으로 데이터 쿼리를 지원하는 일련의 언어 확장입니다. LINQ는 Visual Studio의 다음 버전인 코드 이름 "Orcas"와 함께 릴리스될 예정입니다. 쿼리할 데이터는 XML(LINQ to XML), 데이터베이스(LINQ to SQL, LINQ to Dataset 및 LINQ to Entities를 포함하는 LINQ 지원 ADO.NET), 개체(LINQ to Objects) 등의 형식을 취할 수 있습니다. LINQ 아키텍처는 그림 1에 나와 있습니다.

그림 1 LINQ 아키텍처
그림 1 LINQ 아키텍처 (더 크게 보려면 이미지를 클릭하십시오.)

몇 가지 코드를 살펴보겠습니다. 다음은 출시 예정인 "Orcas" 버전의 C#으로 작성한 LINQ 쿼리 예입니다.

var overdrawnQuery = from account in db.Accounts
                     where account.Balance < 0
                     select new { account.Name, account.Address };

foreach를 사용하여 이 쿼리의 결과를 반복하면 반환되는 각 요소는 잔고가 0보다 작은 계정의 이름과 주소로 구성됩니다.

위 예를 보면 구문이 SQL과 비슷하다는 점을 쉽게 알 수 있습니다. 몇 년 전 Anders Hejlsberg(C# 수석 디자이너)와 Peter Golde는 데이터 쿼리 통합을 개선하기 위한 C# 확장에 대해 고려했습니다. 당시 C# 컴파일러 개발을 이끌던 Peter는 C# 컴파일러를 확장 가능하도록 만들어 특히 SQL과 같은 도메인별 언어의 구문을 검사하는 추가 기능을 지원할 수 있는지에 대한 가능성을 조사했습니다. 반면 Anders는 보다 심층적이고 세부적인 수준의 통합을 고려했습니다. 그는 IEnumerable을 구현한 모든 컬렉션에 대해 작동하는 "시퀀스 연산자", 그리고 IQueryable을 구현한 형식에 대한 원격 쿼리를 검토했습니다. 결국 시퀀스 연산자 개념이 더 많은 호응을 얻어 2004년 초 Anders는 이 아이디어에 대한 자료를 Bill Gates의 Thinkweek에 제출했습니다. 반응은 놀라울 정도로 긍정적이었습니다. 설계 초기 단계에서 간단한 쿼리 구문은 다음과 같았습니다.

sequence<Customer> locals = customers.where(ZipCode == 98112);

여기에서 sequence는 IEnumerable<T>에 대한 별칭이며 "where"는 컴파일러에서 인식하는 특수 연산자였습니다. where 연산자 구현은 조건자 대리자, 즉 bool Pred<T>(T 항목) 형식의 대리자를 받는 일반 C# 정적 메서드였습니다. 핵심적인 개념은 컴파일러가 연산자에 대한 특수한 정보를 갖는다는 것입니다. 이렇게 하면 컴파일러가 정적 메서드를 올바르게 호출하고 대리자를 식에 연결하는 코드를 만들 수 있습니다.

위의 예가 C#에서 쿼리를 위한 최적의 구문이라고 가정해 보겠습니다. 언어 확장이 없다면 C# 2.0에서 이 쿼리는 어떤 형태가 될까요?

IEnumerable<Customer> locals = EnumerableExtensions.Where(customers,
                                                    delegate(Customer c)
        {
            return c.ZipCode == 98112;
        });

이 코드는 지나치게 복잡할 뿐만 아니라 관련 필터(ZipCode == 98112)를 찾기도 무척 힘듭니다. 게다가 이 예는 단순한 편입니다. 여러 가지 필터와 예측 등을 추가할 경우 얼마나 더 알아보기 어렵게 될지 상상해 보십시오. 이러한 복잡성의 근본적인 원인은 무명 메서드에 필요한 구문에 있습니다. 이상적인 쿼리의 경우 식에는 계산할 식만 있으면 됩니다. 그러면 컴파일러는 ZipCode가 정말로 Customer에 정의된 ZipCode를 참조했는지와 같은 정황을 추론합니다. 이 문제는 어떻게 해결할까요? 언어 설계 팀은 특정 연산자에 대한 정보를 언어에 하드코딩하는 방법은 적절치 않다고 판단하고 무명 메서드에 대한 대체 구문을 찾기 시작했습니다. 언어 설계 팀은 대체 구문이 극히 간결하면서 현재 무명 메서드에 대해 필요한 컴파일러 이상의 정보는 요구하지 않기를 원했습니다. 결국 이들은 람다 식을 고안했습니다.


람다 식

람다 식은 여러 가지 측면에서 무명 메서드와 비슷한 언어 기능입니다. 사실 람다 식이 언어에 먼저 추가되었다면 무명 메서드는 필요하지 않았을 것입니다. 람다 식의 기본 개념은 코드를 데이터처럼 처리할 수 있다는 것입니다. C# 1.0에서는 흔히 문자열, 정수, 참조 형식 등을 메서드에 전달하여 메서드가 이러한 값을 이용해 작업하도록 합니다. 무명 메서드와 람다 식은 코드 블록까지 포함하도록 이러한 값의 범위를 확장합니다. 이 개념은 함수형 프로그래밍에서는 일반적인 것입니다.

위의 예에서 무명 메서드를 람다 식으로 대체해 보겠습니다.

IEnumerable<Customer> locals = 
    EnumerableExtensions.Where(customers, c => c.ZipCode == 91822);

여기에는 몇 가지 주목할 만한 점이 있습니다. 우선 람다 식의 간결함을 가능하게 하는 몇 가지 요인에 대해 살펴보겠습니다. 첫째, 구문을 삽입할 때 delegate 키워드를 사용하지 않습니다. 대신 컴파일러에 일반 식이 아님을 알려 주는 새로운 연산자인 =>를 사용합니다. 둘째, 사용법에 따라 Customer 형식을 추론합니다. 이 경우에 Where 메서드의 시그니처는 다음과 비슷할 것입니다.

public static IEnumerable<T> Where<T>(
    IEnumerable<T> items, Func<T, bool> predicate)

Where 메서드의 첫 번째 매개 변수가 IEnumerable<Customer>이며, 따라서 T는 Customer이므로 컴파일러는 "c"가 고객을 지칭한다는 사실을 추론할 수 있습니다. 컴파일러는 이 정보를 사용하여 Customer에 ZipCode 멤버가 있다는 사실도 확인합니다. 마지막으로, return 키워드가 지정되지 않습니다. 구문 형식에서 return 멤버가 생략되지만 이는 단지 구문상의 편의일 뿐입니다. 식의 결과는 여전히 반환 값으로 간주됩니다.

람다 식 역시 무명 메서드와 같이 변수 캡처를 지원합니다. 예를 들어 람다 식 본문 내에서 람다 식을 포함하는 메서드의 매개 변수 또는 지역 변수를 참조하는 것이 가능합니다.

public IEnumerable<Customer> LocalCusts(
    IEnumerable<Customer> customers, int zipCode)
{
    return EnumerableExtensions.Where(customers,
        c => c.ZipCode == zipCode);
}

마지막으로 람다 식은 형식을 명시적으로 지정하고 여러 개의 문을 실행할 수 있는 보다 세부적인 구문을 지원합니다. 예를 들면 다음과 같습니다.

return EnumerableExtensions.Where(customers,
    (Customer c) => { int zip = zipCode; return c.ZipCode == zip; });

긍정적인 사실은 원래 자료에서 제시된 이상적인 구문에 훨씬 더 가까워졌으며, 일반적으로 쿼리 연산자 범위 이상으로 유용한 언어 기능으로 이를 구현할 수 있었다는 점입니다. 현재 어디까지 진행되었는지 다시 한 번 보겠습니다.

IEnumerable<Customer> locals = 
    EnumerableExtensions.Where(customers, c => c.ZipCode == 91822);

여기에는 분명한 문제가 있습니다. 현재 사용자는 Customer에 수행할 수 있는 연산에 대해 생각하는 대신 이 EnumerableExtensions 클래스에 대해 알아야 합니다. 또한 연산자가 여러 개인 경우 사용자는 올바른 구문을 작성하려면 생각을 전환해야 합니다. 예를 들면 다음과 같습니다.

IEnumerable<string> locals = 
    EnumerableExtensions.Select(
        EnumerableExtensions.Where(customers, c => c.ZipCode == 91822), 
        c => c.Name);

Select는 Where 메서드의 결과를 기준으로 연산하지만 외부 메서드입니다. 이상적인 구문은 다음과 같은 형태에 가까울 것입니다.

sequence<Customer> locals = 
    customers.where(ZipCode == 98112).select(Name);

그렇다면 다른 언어 기능을 통해 이상적인 구문에 더 가까워질 수 있을까요?

Back to top

확장 메서드

확장 메서드라고 하는 언어 기능을 사용하면 구문을 크게 개선할 수 있습니다. 확장 메서드는 기본적으로 인스턴스 구문을 사용하여 호출할 수 있는 정적 메서드입니다. 위에 있는 쿼리에서 문제의 원인은 IEnumerable<T>에 메서드를 추가하려고 한다는 데 있습니다. Where, Select 등의 연산자를 추가한다면 모든 기존 및 향후 구현자는 이러한 메서드를 구현해야 합니다. 그러나 이러한 구현의 대부분은 동일합니다. C#에서 "인터페이스 구현"을 공유하는 유일한 방법은 앞서 사용된 EnumerableExtensions 클래스에서 수행한 것처럼 정적 메서드를 사용하는 것입니다.

그 대신 Where 메서드를 확장 메서드로 작성한다고 가정해 보겠습니다. 쿼리는 다음과 같이 다시 작성할 수 있습니다.

IEnumerable<Customer> locals = 
    customers.Where(c => c.ZipCode == 91822);

이 간단한 쿼리의 경우 구문이 이상적 형태에 상당히 근접합니다. 그러나 Where 메서드를 확장 메서드로 작성하는 것이 의미하는 바는 정확히 무엇일까요? 사실 그 의미는 매우 간단합니다. 기본적으로 정적 메서드의 시그니처가 변경되어 다음과 같이 첫 번째 매개 변수에 "this" 한정자가 추가됩니다.

public static IEnumerable<T> Where<T>(
    this IEnumerable<T> items, Func<T, bool> predicate)

또한 메서드는 정적 클래스 내에서 선언되어야 합니다. 정적 클래스는 정적 멤버만 포함할 수 있으며 클래스 선언에서 static 한정자를 사용하여 나타냅니다. 이것이 전부입니다. 이 선언에 따라 컴파일러는 IEnumerable<T>를 구현하는 모든 형식의 인스턴스 메서드와 동일한 구문으로 Where를 호출하도록 허용합니다. 한편 Where 메서드는 현재 범위 내에서 액세스가 가능해야 합니다. 포함하는 형식이 범위 내에 있으면 메서드도 범위 내에 있습니다. 따라서 Using 지시문을 통해 확장 메서드를 범위 내로 가져오는 것이 가능합니다. 자세한 내용은 "확장 메서드" 보충 기사를 참조하십시오.

이제 필터 절에 대해서는 이상적인 구문에 매우 근접했습니다. 그러나 이것이 C# "Orcas" 버전의 전부일까요? 그렇지 않습니다. 예를 약간 확장하여 고객 개체 전체가 아니라 고객의 이름만 예측해 보겠습니다. 앞에서 언급했듯이 이상적인 구문은 다음과 같은 형태를 취합니다.

sequence<string> locals = 
    customers.where(ZipCode == 98112).select(Name);

지금까지 설명한 언어 확장인 람다 식과 확장 메서드를 사용하면 다음과 같이 다시 작성할 수 있습니다.

IEnumerable<string> locals = 
    customers.Where(c => c.ZipCode == 91822).Select(c => c.Name);

이 쿼리의 경우 반환 형식이 IEnumerable<Customer>가 아니라 IEnumerable<string>인데, 이는 select 문에서 고객 이름만 반환하기 때문입니다.

이 방식은 예측이 단일 필드인 경우 매우 훌륭하게 작동합니다. 고객의 이름뿐만 아니라 주소까지 반환하려는 경우를 가정해 봅시다. 이상적인 구문은 다음과 같습니다.

locals = customers.where(ZipCode == 98112).select(Name, Address);
Back to top

익명 형식

기존 구문을 계속 사용하여 이름과 주소를 반환하려는 경우에는 Name과 Address만 포함하는 형식은 없다는 문제에 직면하게 됩니다. 그러나 다음과 같은 형식을 통해 쿼리를 작성할 수 있습니다.

class CustomerTuple
{
    public string Name;
    public string Address;

    public CustomerTuple(string name, string address)
    {
        this.Name = name;
        this.Address = address;
    }
}

그러면 이 형식(여기에서는 CustomerTuple)을 사용하여 쿼리의 결과를 구성할 수 있습니다.

IEnumerable<CustomerTuple> locals = 
    customers.Where(c => c.ZipCode == 91822)
                 .Select(c => new CustomerTuple(c.Name, c.Address));

확실히 이 코드는 필드의 하위 집합을 예측하기 위한 일상적인 코드로 보입니다. 이러한 형식의 이름을 어떻게 지정할지 확실하지 않은 경우가 많습니다. CustomerTuple이 정말 좋은 이름일까요? Name과 Age를 예측해야 하는 경우에는 어떻게 할까요? 이 경우에도 CustomerTuple이 될 수 있습니다. 즉, 문제는 일상적인 코드는 있지만 만드는 형식에 맞는 이름이 없다는 것입니다. 또한 다양한 형식이 필요할 수 있는데, 이러한 경우 형식을 관리하기가 금방 어려워집니다.

익명 형식은 바로 이러한 문제를 해결하기 위한 것입니다. 기본적으로 이 기능을 사용하면 이름을 지정하지 않고 구조적 형식을 만들 수 있습니다. 익명 형식을 사용하여 위의 쿼리를 다시 작성하면 다음과 같습니다.

locals = customers.Where(c => c.ZipCode == 91822)
                       .Select(c => new { c.Name, c.Address });

이 코드는 Name 및 Address 필드가 있는 형식을 암시적으로 만듭니다.

class 
{
    public string Name;
    public string Address;
}

이 형식은 이름이 없으므로 이름으로 참조할 수 없습니다. 필드의 이름은 익명 형식 생성에서 명시적으로 선언할 수 있습니다. 예를 들어 생성되는 필드가 복잡한 식에서 파생되거나 이름이 적절하지 않은 경우 이름을 변경할 수 있습니다.

locals = customers.Where(c => c.ZipCode == 91822)
    .Select(c => new { FullName = c.FirstName + " " + c.LastName, 
                       HomeAddress = c.Address });

여기에서 생성되는 형식에는 FullName 및 HomeAddress라는 필드가 있습니다.

이를 통해 이상적인 구문에 더 접근했지만 문제가 있습니다. 익명 형식을 사용한 곳에서는 계획적으로 지역 변수의 형식을 생략했음을 알 수 있을 것입니다. 익명 형식의 이름을 나타낼 수 없다면 어떻게 이를 사용할 수 있을까요?

Back to top

암시적으로 형식이 지정된 지역 변수

암시적으로 형식이 지정된 지역 변수(또는 줄여서 var)라고 하는 또 다른 언어 기능이 있습니다. 이 기능은 컴파일러가 지역 변수의 형식을 추론하도록 합니다. 예를 들면 다음과 같습니다.

var integer = 1;

여기에서 integer의 형식은 int입니다. 이는 여전히 강력한 형식이라는 점이 중요합니다. 동적 언어에서는 정수의 형식을 나중에 변경할 수 있습니다. 예를 들어 다음 코드는 컴파일되지 않습니다.

var integer = 1;
integer = "hello";

C# 컴파일러는 string을 int로 암시적으로 변환할 수 없다는 정보와 함께 두 번째 줄에서 오류를 보고합니다.

위 쿼리의 경우 이제 다음과 같이 완전한 할당을 작성할 수 있습니다.

var locals =
   customers
       .Where(c => c.ZipCode == 91822)
       .Select(c => new { FullName = c.FirstName + " " + c.LastName, 
                          HomeAddress = c.Address });

지역 변수의 형식은 IEnumerable<?>가 되며 여기에서 "?"는 작성할 수 없는(익명이므로) 형식의 이름입니다.

암시적으로 형식이 지정된 지역 변수는 메서드 내의 지역 변수입니다. 형식을 명시적으로 나타낼 수 없고 "var"은 필드나 매개 변수 형식으로 유효하지 않으므로 이러한 지역 변수는 메서드의 경계를 벗어날 수 없습니다.

암시적으로 형식이 지정된 지역 변수는 쿼리 이외의 분야에서도 유용합니다. 예를 들어 다음과 같이 복잡한 제네릭 인스턴스화를 간소화하는 데 도움이 됩니다.

var customerListLookup = new Dictionary<string, List<Customer>>();

이제 쿼리가 상당한 수준에 이르렀습니다. 이상적인 구문에 가까워졌으며, 범용 언어 기능을 사용하여 여기까지 도달했습니다.

흥미롭게도 많은 사람들이 이 구문을 사용하게 되면서 예측이 메서드 경계를 벗어나도록 허용해야 하는 경우가 많다는 사실을 발견했습니다. 앞에서 살펴보았듯이 이를 위해서는 Select 내에서 개체의 생성자를 호출하여 개체를 구성하면 됩니다. 하지만 설정해야 하는 값을 정확히 받는 생성자가 없는 경우에는 어떻게 해야 할까요?

Back to top

개체 이니셜라이저

이 경우에는 향후 출시될 "Orcas" 버전에서 제공되는 개체 이니셜라이저라고 하는 C# 언어 기능을 사용할 수 있습니다. 개체 이니셜라이저를 사용하면 기본적으로 단일 식에 여러 속성이나 필드를 할당할 수 있습니다. 예를 들어 개체를 생성하는 일반적인 패턴은 다음과 같습니다.

Customer customer = new Customer();
customer.Name = "Roger";
customer.Address = "1 Wilco Way";

이 경우 이름과 주소를 받는 Customer 생성자는 없지만 인스턴스가 생성될 때 한 번 설정할 수 있는 Name과 Address라는 두 가지 속성이 있습니다. 개체 이니셜라이저를 사용하면 다음과 같은 구문으로 동일한 결과를 만들 수 있습니다.

Customer customer = new Customer() 
    { Name = "Roger", Address = "1 Wilco Way" };

이전 CustomerTuple 예에서는 생성자를 호출하여 CustomerTuple 클래스를 만들었습니다. 이제 개체 이니셜라이저를 사용하여 동일한 결과를 얻을 수 있습니다.

var locals = 
    customers
        .Where(c => c.ZipCode == 91822)
        .Select(c => 
             new CustomerTuple { Name = c.Name, Address = c.Address });

개체 이니셜라이저를 사용하면 생성자의 괄호를 생략할 수 있습니다. 또한 필드와 설정할 수 있는 속성을 개체 이니셜라이저의 본문 내에 할당할 수 있습니다.

이제 C#에서 쿼리를 만드는 간결한 구문을 완성했습니다. 또한 확장 메서드를 통해 새 연산자(Distinct, OrderBy, Sum 등)를 추가할 수 있는 확장 가능한 방법과, 각기 유용한 용도가 있는 별도의 언어 기능 집합도 있습니다.

언어 설계 팀은 사용자 의견 수집을 위한 여러 프로토타입을 준비했고 우리는 C#과 SQL에 대한 경험을 모두 갖춘 여러 참가자들로 구성된 사용 연구팀을 구성했습니다. 사용자 의견은 대부분 긍정적이었지만 부족한 부분도 확실히 있었습니다. 특히 개발자들이 SQL에 대한 지식을 적용하기가 어려웠는데, 이는 우리가 이상적이라고 생각한 구문이 개발자들의 분야별 전문 지식과 그다지 잘 부합되지 않았기 때문입니다.

Back to top

쿼리 식

이후 언어 설계 팀은 쿼리 식이라고 하는, SQL과 유사한 구문을 설계했습니다. 예를 들어 지금까지 살펴본 예에 대한 쿼리 식은 다음과 같은 형태를 취할 수 있습니다.

var locals = from c in customers
             where c.ZipCode == 91822
             select new { FullName = c.FirstName + " " +
                          c.LastName, HomeAddress = c.Address };

쿼리 식은 앞서 설명한 언어 기능에 기반을 두고 있으며 이미 살펴본 기본 구문으로 정확히 변환됩니다. 예를 들어 위의 쿼리는 다음과 같이 변환됩니다.

var locals =
   customers
       .Where(c => c.ZipCode == 91822)
       .Select(c => new { FullName = c.FirstName + " " + c.LastName, 
                          HomeAddress = c.Address });

쿼리 식은 from, where, select, orderby, group by, let, join과 같은 여러 가지 "절"을 지원합니다. 이러한 절은 해당하는 연산자 호출로 변환된 다음 확장 메서드를 통해 구현됩니다. 쿼리 절과 연산자를 구현하는 확장 메서드 간의 관계는 매우 밀접하므로 쿼리 구문이 필요한 연산자를 지원하지 않을 경우 손쉽게 이 둘을 조합할 수 있습니다. 예를 들면 다음과 같습니다.

var locals = (from c in customers
              where c.ZipCode == 91822
              select new { FullName = c.FirstName + " " +
                          c.LastName, HomeAddress = c.Address})
             .Count();

이 경우 쿼리는 91822 우편 번호 지역에 거주하는 고객의 수를 반환합니다.

이로써 어느 정도 만족스럽게 처음 시작점과 거의 같은 위치에서 마칠 수 있었습니다. C# 다음 버전의 구문은 여러 가지 새로운 언어 기능의 추가를 통해 지난 몇 년 동안 발전했는데, 결국 이는 2004년 겨울에 제안된 최초의 구문에 매우 근접한 형태가 되었습니다. 추가되는 쿼리 식은 차기 C# 버전의 다른 언어 기능에 바탕을 두고 있으며 SQL 지식이 있는 개발자가 다양한 쿼리 시나리오를 쉽게 읽고 이해할 수 있게 해줍니다.

Trackback 0 Comment 0