상세 컨텐츠

본문 제목

[C#] dynamic

.NET/C#

by 클리엘 클리엘 2021. 4. 30. 16:05

본문

728x90

int나 string와 같은 형식은 컴파일 단계에서 정확한 형식인지를 확인하게 됩니다. 그런데 dynamic은 형식검사를 컴파일 단계가 아닌 프로그램 실행 중에 확인하도록 합니다.

static void Main(string[] args)
{
	int i = 0;
	dynamic j = "aaa";

	int r = i + j;

	Console.WriteLine($"{r}");
}

위 예제는 말도 안 되는 처리를 수행하고 있지만 컴파일은 정상적으로 진행됩니다. 물론 프로그램은 제대로 실행되지 않을 것입니다.

 

dynamic은 오리 타이핑과 관련이 깊습니다. 예컨대 아래와 같은 클래스가 존재할 때

class Car
{
	//
	public void Drive()
	{

	}

	public void Parking()
	{

	}
}


class Bus : Car
{

}

class Truck : Car
{

}

class Bicycle
{
	public void Drive()
	{

	}

	public void Parking()
	{

	}
}

C#세계에서 Car가 되려면 명시적으로 Car를 상속받아야만 합니다. 그래서 Bus나 Truck는 Car에 해당합니다. Bicycle은 Car에서 상속받지 않았기 때문에 Car로 인정될 수 없습니다.

//오류 : Bicycle는 Car가 아님
Car[] cars = new Car[] { new Bus(), new Truck(), new Bicycle() };

하지만 오리 타이핑은 명시적인 상속과는 관련이 없이 어떤 형식의 특징을 갖고 있다면 그 형식으로 간주하게 됩니다. 따라서 예제의 Bicycle은 'Car의 특징인 Drive()와 Parking()이 있으므로 Car로 인정될 수 있다.'라고 보는 것입니다.

dynamic[] cars = new dynamic[] { new Bus(), new Truck(), new Bicycle() };

foreach(dynamic car in cars)
{
	car.Drive();
}

이와 같이 형식검사를 미리 하지 않는다는 특징을 이용해 여러 면에서 dynamic이 사용되는데 그중 하나가 Object형식을 반환하는 COM에서 반환되는 객체를 dynamic으로 받아 이용하는 것입니다. COM은 Component Object Model이라고 하여 특정 목적을 위해 설계된 프로그램의 부품 같은 것이라고 생각할 수 있습니다.

 

COM활용의 대표적인 것 중 하나가 바로 엑셀입니다. Microsoft Excel Object Library라는 COM객체를 이용하면 우리는 엑셀 문서를 읽어내거나 쓸 수 있습니다.(물론 이러한 목적을 위해 반드시 Excel Object Library를 사용해야만 하는 것은 아니지만)

 

비주얼 스튜디오에서 실제 COM객체를 참조하게 되면 tlbimp.exe가 호출되어 RCW(Runtime Callable Wrapper)라는것을 만들어 줍니다. RCW은 C#과 COM사이에 중계역할을 수행해서 C#에서 일반적인 .NET라이브러리를 사용하는 것처럼 COM객체를 다룰 수 있도록 해줍니다.

 

그럼 간단히 엑셀 문서를 생성하는 예를 통해서 dynamic의 활용 부분을 살펴보도록 하겠습니다. 우선 비주얼 스튜디오에서 임의의 프로젝트를 생성한 후 '솔루션 탐색기'에서 '참조'를 선택하고 이어서 마우스 오른쪽 버튼을 눌러 '참조 추가'를 선택해줍니다.

 

참조 관리자창에서 왼쪽 'COM'을 선택하면 사용 가능한 COM라이브러리 목록이 표시되는데 이 중에서 'Micorsoft Excel 16.0 Object Library'를 선택하고 '확인'버튼을 눌러줍니다. 참고로 이 Library가 존재하려면 컴퓨터에 엑셀이 설치되어 있어야 하며 엑셀 버전에 따라 Library의 버전도 차이가 날 수 있습니다.

 

참조를 추가하면 다음과 같이 프로젝트에 Microsoft.Excel에 관한 Library가 추가된 걸 확인할 수 있습니다.

 

프로젝트는 ConsoleApp형태로 해도 상관이 없으나 저는 그냥 WinForm형태의 프로젝트를 생성했습니다. 폼 위에 버튼 하나를 만들고 버튼 클릭이벤트에 다음과 같은 코드를 작성합니다.

private void button1_Click(object sender, EventArgs e)
{
    Microsoft.Office.Interop.Excel.Application excelApp = new Microsoft.Office.Interop.Excel.Application();

    excelApp.Workbooks.Add();

    dynamic workSheet = excelApp.ActiveSheet;
    workSheet.Cells[1, 1] = "hi cliel";
    workSheet.SaveAs(@"C:\Users\Administrator\Downloads\test.xlsx");

    excelApp.Quit();
}

위 프로그램을 실행하고 버튼을 누르면 SaveAs에 명시된 경로에 test.xlsx라는 파일이 생성될 것입니다.

 

예제 코드에서 주목해야 할 부분은 excelApp.ActiveSheet를 지정하고 이것을 dynamic으로 받았다는 점입니다. dynamic이 존재하지 않았던 때에는 저 부분을 다음과 같이 처리했어야 했습니다.

Microsoft.Office.Interop.Excel.Worksheet workSheet = (Microsoft.Office.Interop.Excel.Worksheet)excelApp.ActiveSheet;

앞서 언급했든 COM은 객체를 Object형식으로 반환하기에 이를 받아서 사용하기 위해서는 그에 맞는 형 변환을 해줘야 하는 것입니다. 하지만 dynamic이 생긴 뒤로는 그렇게 할 필요가 없어졌습니다. 게다가 아예 COM에서는 Object가 아닌 dynamic형식을 반환하도록 지원함으로써 다음과 같은 방식의 구현도 가능해졌습니다.

 

ActiveSheet의 반환 형식을 보면 dynamic으로 되어 있음을 알 수 있습니다. 이런 이유로 인해 형 변환 없이 객체를 있는 그대로 받아들이고 있습니다.

 

dynamic형식을 활용하는 또 다른 예로 동적 언어의 객체를 dynamic으로 받아 사용하는 경우를 들 수 있습니다. 동적 언어 중 하나의 파이썬은 프로그램이 실행될 때 코드를 한 줄씩 해석하여 실행하게 됩니다. 놀랍게도 C#안에서 파이썬 같은 동적언어의 코드를 직접 실행할 수 있는데 이를 위해 DLR(Dynamic Language Runtime)을 사용합니다.

 

CLR위에서 동작하는 DLR은 기본적으로 설치되어 있지 않은데 .NET 프레임워크와는 별개의 프로젝트로 진행되고 있습니다.

 

GitHub - IronLanguages/dlr: Dynamic Language Runtime

 

IronLanguages/dlr

Dynamic Language Runtime. Contribute to IronLanguages/dlr development by creating an account on GitHub.

github.com

오픈소스 프로젝트로 진행 중이기 때문에 소스코드를 직접 내려받아 살펴볼 수 있습니다. 일반적으로 DLR을 설치하기 위해서는 NuGet에서 'DynamicLanguageRuntime'이름으로 검색하여 설치하는 것입니다.

 

C#에서 파이썬 코드를 작성해 실행하는 과정을 살펴볼 텐데 이를 위해 Python코드를 실행 가능하도록 해주는 'IronPython'또한 NuGet에서 설치하도록 합니다.

 

모두 설치가 완료되었으면 조금 전 엑셀 예제를 작성했던 버튼 이벤트에 다음과 같은 코드를 작성합니다.

        private void button1_Click(object sender, EventArgs e)
        {
            ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();
            scope.SetVariable("i", 10);
            scope.SetVariable("j", 20);

            ScriptSource source = engine.CreateScriptSourceFromString(@"
class Plus:
    r = 0

    def __init__(self, x, y):
        self.r = x + y

Plus(i, j)");

            dynamic result = source.Execute(scope);
            MessageBox.Show($"{result.r}");
        }

위 예제는 문자열을 통해 직접 파이썬 코드를 만들고 실행하는 것을 보여주고 있습니다. 밑에 Execute메서드를 통해 만들어진 객체를 그대로 dynamic으로 받아 처리한다는 점에 주목해 주세요. 이렇게 받아진 객체를 통해 특정 메서드를 호출해 실행하거나 속성 값을 직접 확인할 수도 있습니다.

728x90

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

[C#] 파일과 디렉토리  (0) 2021.05.03
[C#] dynamic  (0) 2021.04.30
[C#] LINQ  (0) 2021.04.22
[C#] 리플렉션  (0) 2021.04.19
[C#] 대리자(Delegate)  (0) 2021.04.15
[C#] 이벤트(Event)  (0) 2021.04.12

태그

관련글 더보기

댓글 영역