전체 페이지뷰

2017년 1월 13일 금요일

C#, dynamic

C# 4.0에서 새로이 추가된 형식입니다. 이 형식을 사용하면 컴파일 시에 형식 검사를 하지 않고 런타임에 하게 됩니다.

class Program
{
    static void Main(string[] args)
    {
        dynamic dyn = 1;
        object obj = 1;
           
        System.Console.WriteLine(dyn.GetType());
        System.Console.WriteLine(obj.GetType());
    }
}

위와 같은 코드를 실행하면
System.Int32
System.Int32

의 결과가 출력됩니다. 만약 WriteLine문 위에 다음의 두 줄을 추가하면 어떻게 될까요?
dyn = dyn + 3;
obj = obj + 3;

비주얼 스튜디오에 오류를 뜻하는 빨간 줄이 표시됩니다. 그러나 둘 다는 아니고 obj+3에만 그렇습니다. obj 줄을 지우고 dyn의 값을 찍어보면 4가 무사히 출력됩니다. 런타임에 dyn은 int형으로 결정된 것입니다.

그런데 왜 컴파일 시에는 오류를 확인할 수도 없는 이런 형식을 만든 것일까요?

Duck Typing

덕타이핑에 대해서는 많이들 알고 계실겁니다. 클래스 상속이나 인터페이스 상속으로 타입을 구분하는 것이 아니고 어떤 타입에 걸맞는 변수나 메소드를 가지고 있을 경우 그 객체를 해당 형식으로 인정하는 기법을 말합니다.(덕 타이핑 참고)

결국 프로그래밍 시 좀 더 유연한 방식이 가능해지도록 하는 도구인데, 사실 C#과는 딱히 관련이 없습니다. 주로 동적언어와 관련된 방식이죠.

그러나, 세상에는 C#만 있는 것이 아닙니다.

COM과 .NET 사이의 dynamic

COM이란 Component Object Model의 약자로, 1993년에 Microsoft가 발표한 응용 프로그램 2진 인터페이스입니다. 현재는 .NET에 많이 대체 되었지만 오래된만큼 여전히 여러 분야에서 쓰이고 있는 기술입니다. 대표적으로 마이크로소프트 오피스가 COM을 이용한 제품입니다.

C#과 같은 .NET언어는 COM요소와 RCW(Runtime Callable Wrapper)를 매개로 해서 상호작용합니다. 이 때 RCW에서 dynamic 타입이 내부적으로 사용됩니다. (우리가 코드에 직접 사용하는 것은 아닙니다.)

이제 C#에서 COM 컴포넌트를 프로젝트에 추가하고 코드를 작성하는 예를 엑셀을 이용해서 살펴봅니다.

먼저 늘 하듯이 새 프로젝트를 만들고,
솔루션 탐색기에서 참조 위에 우클릭 한 뒤 나타나는 메뉴에서 '참조 추가'를 클릭합니다.

새롭게 나타나는 창의 왼편에서 'COM' -> '형식 라이브러리'를 선택하고, 구성요소목록에서
'Microsoft Excel 15.0 Object Library(저는 15.0인데 버전에 따라 다들 다르겠죠)'의 체크박스를 체크하고 '확인'을 누릅니다.

그러면 우측 솔루션 탐색기에 'Microsofe.Excel.COre'와 'Microsoft.Office.Interop.Excel' 어셈블리가 추가됩니다.


이제 이것을 이용해서 코드를 작성하겠습니다.

using System;
using Excel = Microsoft.Office.Interop.Excel;
namespace ConsoleApplication2
{
    class Program
    {
        public static void WriteToExcel(string[,] data, string savepPath)
        {
            Excel.Application excelApp = new Excel.Application();
            excelApp.Workbooks.Add();
            Excel._Worksheet workSheet = excelApp.ActiveSheet;
            for (int i = 0; i < data.GetLength(0); i++)
            {
                workSheet.Cells[i + 11= data[i, 0];
                workSheet.Cells[i + 12= data[i, 1];
            }
            workSheet.SaveAs(savepPath + "\\csharpStudy.xlsx");
            excelApp.Quit();
        }
        static void Main(string[] args)
        {
            string savePath = System.IO.Directory.GetCurrentDirectory();
            string[,] arr = new string[,]
            {
                {"ABC","1" },
                {"DEF","2" },
                {"GHI","3" }
            };
            Console.WriteLine("Creating Excel doc. on " + savePath);
            WriteToExcel(arr, savePath);
            
        }
    }
}
cs


결과)
Creating Excel doc. on c:\users\user\documents\visual studio 2015\Projects\ConsoleApplication2\ConsoleApplication2\bin\Debug

위와 같이 콘솔에 출력되는 한편 해당 폴더에 엑셀파일이 생성됩니다.




동적 언어와 dynamic

최근 동적 언어가 각광을 받아왔습니다. 저도 매우 좋아하는 파이썬이나 루비 등이 동적 언어에 속하죠. C#의 CLR은 컴파일 언어에만 적용되기 때문에 마이크로소프트는 동적 언어를 실행가능하게 해주는 DLR(Dynamic Language Runtime)을 내놓았습니다. 이 DLR을 통해 C# 코드 내에서 직접 파이썬 코드를 실행 가능합니다.
동적 언어 아키텍처 개요
출처 MSDN


(비주얼 스튜디오에서 Python을 사용하려면 Visual Studio의 파이썬 툴인 IronPython을 받아야 합니다.)

DLR에서 동적 언어를 직접 다루는데 필요한 클래스들을 몇개 살펴봅시다.

ScriptRuntime
동적 언어 호스팅 시작점입니다.

ScriptScope
동적 언어를 위한 네임스페이스

ScriptEngine
코드를 실행하는 엔진

ScriptSource
소스 코드를 읽는 메소드와 그를 이용하는 메소드 제공

CompiledCode
컴파일된 코드. 한번 컴파일 후 여러번 재사용

자 이제 이것과 dynamic을 이용해서 C# 코드 내에 파이썬을 사용해 보겠습니다.

프로젝트를 생성하고 DLR과 IronPython 어셈블리를 추가합니다.
위에서 한 방식으로 솔루션 탐색기의 '참조'를 우클릭하여 '참조 추가'합니다.
참조관리자가 뜨면 '찾아보기'를 클릭하고,

아래 다섯 개의 dll 파일을 추가해 줍니다.
그리고, 코드를 작성합니다.

using System;
 
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
 
namespace PythonExam
{        
    class Program
    {
        static void Main(string[] args)
        {
            ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();
            scope.SetVariable("n""LateDreamer");
            scope.SetVariable("p""010-1234-5678");
            ScriptSource source = engine.CreateScriptSourceFromString(@"
class NameCard:
    name=''
    phone=''
    def __init__(self, name, phone) :
        self.name = name
        self.phone = phone
    def printNameCard(self):
        print(self.name + ' : ' + self.phone)
NameCard(n,p)
");
            dynamic result = source.Execute(scope);
            result.printNameCard();
 
            Console.WriteLine("{0} : {1}", result.name, result.phone);
        }
    }
}
cs

결과)
LateDreamer : 010-1234-5678
LateDreamer : 010-1234-5678



댓글 없음:

댓글 쓰기