Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C#] 동적 언어와의 상호 운용성을 위한 dynamic 형식 본문

Programming/C#

[Programming/C#] 동적 언어와의 상호 운용성을 위한 dynamic 형식

scii 2020. 9. 27. 11:06

파이썬(Python)과 루비(Ruby)는 최근 유행하고 있는 동적 언어(Dynamic Language 또는 Dynamic Typed Language)이다. 

CLR(Common Language Runtime) 은 IL(Intermediate Language)로 컴파일할 수 있는 언어들은 지원하지만, 파이썬이나 루비처럼 실행할 때 코드를 해석해서 실행하는 방식의 동적 언어는 지원할 수 없다. 그래서 마이크로소프트는 동적 언어를 실행할 수 있도록 해주는 플랫폼인 DLR(Dynamic Language Runtime)을 선보였다.

DLR은 CRL위에서 동적하며, 파이썬이나 루비와 같은 동적 언어를 실행할 수 있다. DLR의 장점은 그저 동적 언어를 .NET 플랫폼에서 실행할 수 있다는 정도에서 그치지 않는다. DLR은 파이썬이나 루비 같은 동적 언어의 코드에서 만들어진 객체에 C#이나 VB같은 정적 언어의 코드에서 접근할 수 있게 해준다. 한마디로, C#코드에서 직접 파이썬이나 루비 코드를 실행하고 그 결과를 받아볼 수 있다는 이야기이다.

또한 CLR 입장에서 보면 DLR API를 기반으로 구현된 동적 언어라도 호스팅(Hosting) 할 수 있다는 장점이 있다. 가령 파이썬을 줄곧 사용해오다 파이썬에는 없는 라이브러리가 루비에 있는 경우, C# 프로그래머는 별도의 학습 없이도 바로 루비 라이브러리를 이용하는 코드를 호스팅할 수 있다.

COM과 .NET의 상호 운용성 문제에 사용했던 dynamic을 CLR과 DLR 사이의 상호 운용성 문제을 해결하는데 사용할 수 있다. 미리 형식 검사를 할 수 없는 동적 형식 언어에서 만들어진 객체를 C#의 dynamic 형식이 받아낼 수 있기때문이다.

클래스 설명
ScriptRuntime 동적 언어를 호스팅하는 시작점. ScriptRuntime 클래스는 참조된 어셀브리나 전역 객체 같은 전역 상태를 나타내며 하나의 .NET AppDomain 안에 여러 개의 ScriptRuntime 인스턴스를 만들 수 있다.
ScriptScope 기본적으로 네임스페이스를 나타낸다. 호스트(즉, C# 코드)는 ScriptScope 객체 안에 동적 언어 코드에서 사용하는 변수에 값을 대입하거나 읽을 수 있다.
ScriptEngine 스크립트 엔진은 언어의 구문을 나타내는 일꾼이다. 스크립트 엔진은 코드를 실행하고 ScriptScope와 ScriptSource를 생성하는 다양한 방법을 제공한다.
ScriptSource 이 클래스는 소스 코드를 읽어들이는 여러 메소드와 읽어들인 소스 코드를 다양한 방법으로 실행하는 메소드들을 제공한다.
CompiledCode 이 클래스는 컴파일된 코드를 나타낸다. 한번 컴파일해놓고 여러 번 반복해서 실행하는 코드를 나타내는 데 사용된다.

위 표의 클래스들은 C# 호스트 코드에서 게스트 코드를 실행할 때 다양한 방법으로 조합하여 사용할 수 있다. 우선 ScriptRuntime 객체는 소스 코드 "파일"의 경로를 넘겨받아 실행할 수 있다.

// 파이썬 소스 코드 파일을 ScriptRuntime 객체가 읽어 실행하는 예
ScriptRuntime runtime = Python.CreateRuntime();
dynamic result = runtime.ExcuteFile("namecard.py");

프로그램을 실행할 때 생성한 문자열에 담긴 동적 언어 코드도 실행할 수 있다. ScriptEngine, ScriptScope, ScriptSource 클래스를 이용하면 된다.

// 문자열에 담겨 있는 파이썬 코드를 실행하는 예
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
scope.SetVariable("n", "test");
scope.SetVariable("p", "000-123-1234");

ScriptSource source = engine.CreateScriptSourceFromString(
        @"
class NameCard:
    name = ''
    phone = ''
    
    def __init__(self, name, phone):
        self.name = name
        self.phone = phone
        
    def print_name(self):
        print('{0}, {1}'.format(self.name, self.phone))
        
NameCard(n, p)
");

dynamic result = source.Execute(scope);
result.print_name();

Console.WriteLine("{0}, {1}", result.name, result.phone);

마지막 줄에서 dynamic 형식은 ScriptRuntime을 이용해서 소스 파일에 담긴 코드를 실행하든 ScriptEngine, ScriptScope, ScriptSource를 이용해서 문자열에 담긴 코드를 그 자리에서 실행하든 C# 코드가 호스팅하고 있는 파이썬 코드 안에서 만들어진 객체를 그대로 받아낸다. 이렇게 받아낸 파이썬 출신의 객체는 C# 코드에서 직접 메소드를 호출할 수도 있고 필드에 접근하는 것도 가능하다.


C#에서 Python운용하는 예제

1. IronPython은 다운받고 설치한다. IronPython은 .NET Framework에서 구동이 가능한 파이썬 엔진이라고 생각하면 된다.

https://ironpython.net/

 

IronPython.net /

IronPython is an open-source implementation of the Python programming language which is tightly integrated with the .NET Framework. IronPython can use the .NET Framework and Python libraries, and other .NET languages can use Python code just as easily. Why

ironpython.net

2. C# 콘솔 프로젝트를 만든다.

프로젝트 이름을 정한다.

3. WithPython 프로젝트에 DLR과 IronPython 어셈블리를 추가하자. 솔루션 탐색기에서 WithPYthon 프로젝트의 '참조' 항목에서 마우스 우측 클릭 후 컨텍스트 메뉴를 띄운다. 그 후 '참조 추가' 항목을 클릭한다. 다음과 같이 참조 추가 창이 나타나면 [찾아보기] 탭을 선택하여 아이언 파이썬이 설치된 디렉토리를 찾아 다음 그림과 같이 다섯 개의 어셈블리(IronPython.dll, IronPython.Modules.dll, Microsoft.Dynamic.dll, Microsoft.Scripting.dll, Microsoft.Scripting.Metadata.dll) 를 선택한다.

References에서 마우스 우측 클릭하여 Add References... 를 선택한다.
Browse... 버튼을 클릭
해당 어셈블리들을 추가한다.
어셈블리들이 추가된 모습

4. WithPython 프로젝트의 Program.cs 파일의 이름을 MainApp.cs로 변경하고 코드를 작성한다.

using System;

using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;

namespace WithPython
{
    class MainApp
    {
        static void Main(string[] args)
        {
            ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();
            scope.SetVariable("n", "testName");
            scope.SetVariable("p", "000-123-1234");

            ScriptSource source = engine.CreateScriptSourceFromString(
                @"
class NameCard:
    name = ''
    phone = ''

    def __init__(self, name, phone):
        self.name = name
        self.phone = phone

    def print_namecard(self):
        print '{0}, {1}'.format(self.name, self.phone)

NameCard(n, p)
");

            dynamic result = source.Execute(scope);

            result.print_namecard();

            Console.WriteLine("{0}, {1}", result.name, result.phone);
        }
    }
}


/* 결과

testName, 000-123-1234
testName, 000-123-1234

*/
Comments