C# dynamic 형식

김민구·2025년 5월 27일
0

C#

목록 보기
27/31

C#의 dynamic 형식 파헤치기

이번 글에서는 C# 4.0부터 도입된 dynamic 형식에 대해 알아보겠습니다. dynamic 형식은 C# 언어에 동적인 특성을 부여하여 컴파일 시점의 정적 형식 검사를 우회하고, 멤버 호출 등을 런타임에 결정할 수 있게 해줍니다. 이 기능은 2008년 10월 LA에서 열린 PDC(Professional Developers Conference)에서 마이크로소프트의 C# 팀을 이끌던 앤더스 헤일스버그가 발표했으며, 모든 프로그래머에게 환영받는 기능인 동시에 논란을 일으키기도 했습니다.

dynamic 형식을 이해하면 다음과 같은 내용을 알 수 있습니다:

  • dynamic 형식의 정의
  • dynamic 형식을 이용한 변수 선언 및 사용 방법
  • dynamic 형식을 이용한 COM 객체 처리 방법
  • 파이썬과 같은 동적 언어와의 상호 운용성 확보 방법

자, 그럼 dynamic 형식을 자세히 살펴봅시다.

유연한 타입: dynamic 변수

일반적으로 C#에서는 변수를 선언할 때 형식을 명시해야 합니다 (정적 타이핑). 예를 들어, MyClass obj = new MyClass();와 같이 선언합니다. 이 경우 obj 변수는 MyClass 형식으로 고정되며, MyClass에 정의되지 않은 멤버에 접근하려 하면 컴파일 오류가 발생합니다.

하지만 dynamic 형식을 사용하면 변수의 형식에 대한 컴파일 시점 검사가 완화됩니다. dynamic obj = new MyClass();와 같이 선언하면, 이 obj 변수에 대해 어떤 멤버(메서드, 속성 등)를 호출하더라도 컴파일러는 오류를 보고하지 않습니다. 대신 해당 멤버의 존재 여부 및 호출 가능성은 프로그램이 실행되는 런타임 시점에 결정됩니다. 이는 코드를 더 유연하게 만들 수 있지만, 런타임 오류의 위험을 내포하기도 합니다.

오리처럼 걷고 헤엄치는 객체들: 덕 타이핑 (Duck Typing)

덕 타이핑은 프로그래밍 패러다임 중 하나로, 객체의 형식이 아닌 객체가 가진 메서드나 속성으로 그 객체의 가능성을 판단하는 방식입니다. "오리처럼 걷고, 오리처럼 꽥꽥거리면, 그건 오리다(If it walks like a duck and quacks like a duck, it's a duck)."라는 말에서 유래했습니다.

C#에서 dynamic 형식은 이러한 덕 타이핑과 유사한 방식으로 동작할 수 있게 해줍니다. 소스에서는 Duck, Mallard (청둥오리), Robot 클래스를 예로 들고 있습니다. DuckRobot 클래스는 모두 Walk(), Swim(), Quack() 메서드를 가지고 있습니다.

class Duck
{
    public void Walk() { /* ... */ }
    public void Swim() { /* ... */ }
    public void Quack() { /* ... */ }
}

class Robot
{
    public void Walk() { /* ... */ }
    public void Swim() { /* ... */ }
    public void Quack() { /* ... */ }
}

만약 이 객체들을 Duck[] 배열에 담으려 한다면 Robot 객체는 Duck 형식이 아니므로 오류가 발생할 것입니다. 하지만 dynamic[] 배열을 사용하면, 배열 안에 Duck, Mallard, Robot 객체를 모두 담을 수 있습니다.

dynamic[] arr = new dynamic[] { new Duck(), new Mallard(), new Robot() };

foreach (dynamic duck in arr)
{
    // 각 객체의 Walk(), Swim(), Quack() 메서드를 호출
    duck.Walk();
    duck.Swim();
    duck.Quack();
}

dynamic 형식으로 선언된 duck 변수는 컴파일 시점에 형식이 결정되지 않으므로, 반복문 안에서 Walk(), Swim(), Quack() 메서드를 호출하더라도 컴파일 오류가 나지 않습니다. 각 객체의 실제 형식은 런타임에 확인되며, 해당 메서드가 존재하면 성공적으로 호출되고, 존재하지 않으면 런타임 오류가 발생합니다. 이처럼 dynamic을 통해 객체의 명시적 형식보다는 객체가 가진 능력(메서드)에 기반한 코드를 작성할 수 있습니다.

레거시 시스템과의 만남: COM 상호 운용성

COM(Component Object Model)은 마이크로소프트의 소프트웨어 컴포넌트 모델이며, OLE, ActiveX 등 다양한 기술의 기반이 되었습니다. .NET 발표 이후 COM이 사라질 것이라는 예상도 있었지만, 여전히 많은 프로그래머가 COM을 사용하고 있으며 마이크로소프트에서 출시하는 대부분의 제품은 COM을 지원합니다.

C#에서 COM 객체를 다루는 것은 종종 복잡했습니다. 특히 메서드 호출 시 많은 선택적 매개변수에 대해 Type.Missing 등을 넘겨야 하는 경우가 많았습니다. dynamic 형식이 도입되면서 COM 객체와의 상호 운용성이 크게 간소화되었습니다.

소스에서는 Microsoft Excel COM 컴포넌트를 다루는 예제를 통해 이를 보여줍니다. 예제는 Excel 애플리케이션을 실행하고, 워크북을 추가하며, 특정 셀에 데이터를 입력하고, 파일을 저장한 후 Excel을 종료하는 과정을 포함합니다. 소스 코드를 직접 비교해 보면, dynamic을 사용한 방식(NewWay)이 이전 방식(OldWay)에 비해 COM 객체 멤버 접근 및 메서드 호출 시 필요한 코드의 복잡성을 줄여주는 것을 확인할 수 있습니다 (특히 Type.Missing 인자 전달 등). 이 예제를 실행하려면 Microsoft Excel이 설치되어 있어야 합니다.

동적인 세상과의 소통: 동적 언어 상호 운용성

C#은 dynamic 형식을 통해 파이썬, 루비와 같은 동적 언어와의 상호 운용성도 지원합니다. 이는 DLR(Dynamic Language Runtime)이라는 .NET 프레임워크의 일부 구성 요소 덕분입니다. DLR은 마이크로소프트가 오픈 소스로 공개하여 개발하고 있으며, IronPython과 같은 DLR 기반 언어의 소스 코드를 내려받아 직접 빌드하여 C#에서 사용할 수 있습니다.

소스에서는 IronPython을 이용하여 C# 애플리케이션 내에서 파이썬 코드를 실행하고, 파이썬 코드로 생성된 객체를 C#에서 다루는 예제를 보여줍니다. 예제를 실행하기 위해서는 먼저 IronPython 패키지를 설치해야 합니다.

예제 코드는 다음과 같습니다:
1. 파이썬 스크립트 엔진(Python.CreateEngine())을 생성합니다.
2. 스크립트 스코프(CreateScope())를 생성하고 변수를 설정합니다 (name, phone).
3. 실행할 파이썬 코드를 문자열로 작성합니다. 이 코드에는 NameCard라는 클래스 정의와 인스턴스 생성 코드가 포함되어 있습니다.
4. ScriptSource.CreateScriptFromString() 메서드를 사용하여 파이썬 코드를 소스 객체로 만듭니다.
5. source.Execute(scope)를 호출하여 파이썬 코드를 실행하고, 그 결과를 dynamic 형식의 result 변수에 저장합니다. 파이썬 코드의 NameCard(n, p) 호출 결과인 NameCard 객체가 result에 담깁니다.
6. result 변수는 dynamic 형식이기 때문에, C# 코드에서 result.printNameCard()와 같이 파이썬 객체의 메서드를 직접 호출하거나 result.name, result.phone과 같이 필드에 접근할 수 있습니다.

이처럼 dynamic 형식을 사용하면 C#과 같은 정적 언어가 파이썬과 같은 동적 언어와 자연스럽게 상호작용할 수 있게 됩니다.

profile
C#, Unity

0개의 댓글