상세 컨텐츠

본문 제목

[C#] dynamic 형식

.NET/C#

by 클리엘 클리엘 2021. 10. 21. 11:28

본문

728x90

C# 4.0에 처음 등장한 dynamic형식은 형식검사를 컴파일 단계가 아닌 프로그램 실행 중에 합니다. 이러한 특징 때문에 아래와 같은 엉터리 코드도 정상적인 컴파일이 이루어집니다.

class Program
{
    static void Main(string[] args)
    {
        dynamic d = 1;

        d.WriteLine("안녕하세요.");
    }
}

형식 검증이 컴파일 단계에서 이루어지는 형태가 아니다 보니 실제 WriteLine() 메서드가 존재하는지의 여부는 확인하지 않기 때문입니다.

컴파일 단게에서 진행되는 강력한 형식검사를 받지 않게 된다는 것은 곧 프로그램의 형식을 지키지 않는 코드의 작성이 가능하다는 얘기가 됩니다. 예를 들어 다음과 같은 경우

class Car
{
    public void Drive()
    {
        WriteLine("자동차 주행");
    }
}

class Sedan : Car
{
}

class Truck : Car
{
}

Sedan과 Truck은 Car에서 상속받았으므로 Car로 인정될 수 있습니다. 그래서 다음과 같이 메서드를 호출이 가능합니다.

class Program
{
    static void Main(string[] args)
    {
        Drive(new Sedan());
    }

    static void Drive(Car d)
    {
        d.Drive();
    }
}

하지만 Drive()라는 메서드를 가진 새로운 형식이 만들어지더라도

class Bike
{
    public void Drive()
    {
        WriteLine("자전거 주행");
    }
}

Car에서 상속받지 않으면 Car로 인정되지 않습니다. 따라서 Program.Drive() 메서드를 통해 Bike라는 객체가 전달될 수 없습니다.

Bike를 처리할 수 있는 별도의 메서드를 만들거나 해야 하는데 dynamic을 사용하면 그런 번거로움을 해결할 수 있습니다.

class Program
{
    static void Main(string[] args)
    {
        Drive(new Bike());
    }

    static void Drive(dynamic d)
    {
        d.Drive();
    }
}

형식검사가 프로그램실행중에 이루어지므로 어쨌건 객체가 Drive()라는 메서드를 가지고 있기만 하면 됩니다. 단순 데이터 형식의 변환마저도 별도의 캐스팅이 필요 없을 정도로 dynamic은 형식과 관련해서 매우 자유롭습니다.

class Program
{
    static void Main(string[] args)
    {
        dynamic i = 7;
        dynamic s = "hello";

        int _i = i;
        int _s = s;
    }
}

형식을 일치시키기 위해 클래스나 인터페이스에서 상속을 받고 그것을 사용하는 규칙따위는 dynamic에서는 존재하지 않습니다. 장황하게 설명했지만 핵심은 형식검사가 프로그램 실행 중에 대입되는 데이터의 형태에 따라 그 형식이 결정된다는 것입니다.

 

어떤 형식이건 사용하려는 기능이 마련되어 있기만 하면 그것을 실행하는데는 아무런 문제가 없는데 그렇다고 해서 무작위로 dynamic을 남발되어서는 안 됩니다. 오히려 형식검사를 하지 않는다는 점이 나중에는 프로그램을 유지관리하는데 어려움을 줄 수 있기 때문입니다.

 

물론 dynamic의 형식을 바라보는 유연성 때문에 dynamic을 사용해서 얻는 이득이 훨씬 큰 경우도 있습니다. 그 중 하나가 DLR(동적 언어 런타임)을 사용할 때입니다.

 

DLR(동적 언어 런타임)은 .NET Framework 4에 도입된 API로 dynamic 형식을 통해 IronPython 및 IronRuby와 같은 동적 프로그래밍 언어를 구현할 수 있습니다. CLR은 IL로 컴파일이 가능한 다른 언어는 지원하지만 실행단계에서 코드가 해석되어 실행되는 형태인 '동적 언어'는 지원할 수 없습니다. 이러한 문제를 해결하기 위해 CLR위에서 동작하는 DLR이 등장하였는데 C#이나 VB.NET과 같은 언어에서 이들 언어의 코드를 실행하거나 실행 후의 결과까지도 다룰 수 있도록 지원합니다.

 

C#에서 동적 언어를 사용하기 위해 사용하는 DLR에는 다양한 클래스들이 있는데 그 중에서 ScriptRuntime을 사용하면 동적언어 소스코드가 들어있는 파일을 직접 실행하고 결과를 받을 수 있습니다.

 

우선 IronPython을 사용하기 위해 프로젝에 IronPython과 DLR의 NuGet Package를 설치해야 합니다. 패키지를 설치하고 고 나면 Python코드는 임의의 값을 출력하도록 아래와 같은 내용으로 sample.py파일을 만들어 둡니다.

i = 10
j = 20

print i + j

위에서 만든 파일을 C#에서 실행하기 위해 ScriptRuntime클래스를 사용합니다.

private static void Main(string[] args)
{
    ScriptRuntime runtime = Python.CreateRuntime();
    dynamic result = runtime.ExecuteFile("sample.py");

    WriteLine($"결과값 : {result.i}");
}

ExecuteFile() 메서드를 통해 python파일을 실행하고 나면 실행에 대한 객체를 반환하고 이를 dynamic형식의 result변수를 받고 있습니다. python에서는 i와 j의 변숫값을 가지고 있지만 이를 활용하기 위한 형식이 C#에서는 존재하지 않고 '일단 실행을 하고 그 결과로 되돌아오는 걸 봐야 알 것 같다.'는 취지로 dynamic을 사용하는 것입니다.

 

이미 언급했듯이 파일을 통한 실행뿐 아니라 아예 코드를 직접 다루는 것도 가능하므로 해당 부분도 살펴보겠습니다.

class Program
{
    private static void Main(string[] args)
    {
        ScriptEngine engine = Python.CreateEngine();
        ScriptScope scope = engine.CreateScope();

        //여기에 python코드 작성
        ScriptSource source = engine.CreateScriptSourceFromString(@"
def sum(i,j):
return i+j
");

        source.Execute(scope);
        dynamic result = scope.GetVariable("sum");

        WriteLine(result(10, 20));
    }
}

ScriptEngine은 코드 실행에 필요한 ScriptSscope와 ScriptSource을 생성하는 클래스이며 ScriptSource는 실제 주어진 코드를 실행하는 클래스입니다. 코드를 실행할 때는 Execute() 메서드를 사용하는데 매개변수로 주어진 ScriptScope클래스를 사용해 코드 안에서 동작하는 함수나 변수에 접근하게 됩니다.

 

예제에서도 sum이라는 python함수에 접근하고 있는데 이때 dynamic형식을 사용해 python내부의 객체를 그대로 받아 실행하고 있습니다.

 

dynamic은 동적 언어뿐만 아니라 COM과의 상호운영성을 위해서도 사용할 수 있습니다. COM은 Component Object Model로서 마이크로소프트의 소프트웨어 컴포넌트 규격을 말하는데 프로그램에 필요한 각종 공통기능들이 COM으로 만들어지고 프로그램에서는 특정 기능이 필요한 곳을 COM을 사용해 구현함으로써 좀 더 빠르게 프로그램을 만들 수 있었습니다. 오래된 기술로서 현재도 많이 사용되고 있지만 점차 .NET의 라이브러리로 대체되고 있습니다.

 

.NET에서 COM은 RCW(Runtime Callable Wrapper)를 통해 사용됩니다. RCW은 COM과의 중계역할을 해주는 것으로  .NET이 제공하는 Type Library Impoter(tlbimp.exe)로 만들어 지고 .NET언어에서는 RCW로 COM의 API를 호출하게 됩니다.

 

그러나 C# 같은 언어에서 COM을 사용하는 게 쉽지만은 않은데 일단 COM자체가 결괏값으로 object형식을 반환하다 보니 object로 받아 실제 형식으로 변환해 주는 과정을 거쳐야 합니다. 하지만 이 번거로움을 dynamic으로 해결할 수 있는 것입니다.

 

COM을 사용하는 대표적인 경우가 Excel문서를 만들거나 편집하는 경우입니다. 이때에도 dynamic이전에는 아래와 같이 형식 변환이 필요했지만

((Excel.Range)excelApp.Cells[1, 1]).Value2 = "Name";
Excel.Range range2008 = (Excel.Range)excelApp.Cells[1, 1];

dynamic을 통해 excelApp을 다루게 되면 형식 변환의 번거로움을 무시할 수 있습니다.

excelApp.Cells[1, 1].Value = "Name";
Excel.Range range2010 = excelApp.Cells[1, 1];
728x90

'.NET > C#' 카테고리의 다른 글

[C#] Thread(스레드)와 Task(태스크)  (0) 2021.10.24
[C#] 파일과 디렉터리 다루기  (0) 2021.10.21
[C#] dynamic 형식  (0) 2021.10.21
[C#] 리플렉션과 애트리뷰트  (0) 2021.10.20
[C#] LINQ  (0) 2021.10.19
[C#] 람다식  (0) 2021.10.19

태그

관련글 더보기

댓글 영역