본문 바로가기

C#

[ 이것이 C#이다 ] Chapter 17 . dynamic 형식

 [ 학습 흐름 ]
  1. dynamic 형식
  2. 덕 타이핑
  3. COM과 .NET 사이의 상호 운용성
  4. 동적 언어와의 상호 운용성

[ dynamic 형식 ]

[ dynamic 형식 ]

class MyClass
{
	public void FuncAAA()
    {
    	//Do Something
    }
}

class MainApp
{
	static void Main(string[]args)
    {
    	dynamic obj=new MyClass();
        obj.FuncAAA();
        //obj가 dynamic 형식으로 선언 , 메소드가 선언되지 않았지만 컴파일러 검사를 피해간다
        obj.FuncBBB();
    }
}

- 형식 검사를 하는 시점이 프로그램 실행중인 데이터 형식

- dynamic 형식을 만나면 컴파일러는 형식검사를 프로그램 실행중으로 미루게 된다 .

- C#컴파일러가 제공하는 강력한 형식검사의 이점과 상충하는 특징이다 .


[ 덕타이핑 ]

- 객체지향 프로그래밍에서는 어떤 형식이 인정받기 위해서는 그 형식의 조상중 인정받기 위한 대상이 있어야 한다 .

- 덕타이핑에서는 상속이 아닌 , 메서드의 구현으로 형식의 인정을 받는다 . ex) 헤엄치다 / 꽥꽥대다 / 걷다 등

using System;

namespace ConsoleApp;

class Duck
{
    public void Walk()
    { Console.WriteLine(this.GetType()+".Walk"); }
    public void Swim()
    { Console.WriteLine(this.GetType() + ".Swim"); }
    public void Quack()
    { Console.WriteLine(this.GetType() + ".Quack"); }
}

class Mallard : Duck { }

class Robot
{
    public void Walk()
    { Console.WriteLine(this.GetType() + ".Walk"); }
    public void Swim()
    { Console.WriteLine(this.GetType() + ".Swim"); }
    public void Quack()
    { Console.WriteLine(this.GetType() + ".Quack"); }
}

class MainApp
{ 
    static void Main(string[] args)
    {
        dynamic[] arr = new dynamic[] { new Duck(), new Mallard(), new Robot() };
        foreach(dynamic duck in arr)
        {
            Console.WriteLine(duck.GetType());
            duck.Walk();
            duck.Swim();
            duck.Quack();
            Console.WriteLine();
        }    
    }
}

- 인터페이스 설계를 위한 추상화는 연습과 경험이 많이 필요하다 .

- 잘못 설계된 인터페이스는 파생 클래스 , 형제 클래스 등 줄줄이 수정이 필요한 경우가 발생한다 .

- 이럴때 덕타이핑은 상속관계를 이용하지 않기에 프로그램 동작에 관여하는 부분만 손대면 된다 .

- 다만 , 덕타이핑은 비주얼 스튜디오의 리팩토링등의 기능을 사용할 수 없다 .


[ COM과 .NET 사이의 상호 운용성을 위한 dynamic 형식 ]

[ COM ]

- Component Object Model . 마이크로소프트의 소프트웨어 컴포넌트 규격을 말한다 .

- OLE / ActiveX / COM+와 같은 파생 규격들이 모두 COM을 바탕으로 만들어졌다 .

- COM은 부품역할을 하는 소프트웨어이다 .

- COM 컴포넌트는 그래픽 프로그래밍이 서툰 프로그래머가 화려한 차트 기능과 액셀 문서의 읽고 씀을 가능하게 한다 .


[ RCW ]

- .NET 언어들은 RCW(Runtime Callalbe Wrapper)를 통해 COM 컴포넌트를 사용하게 한다 .

- RCW는 .NET이 제공하는 Type Library Importer( tlbimp.exe )를 이용해서 만들수 있다 .

- 비주얼 스튜디오를 사용 , COM 객체를 프로젝트 참조에 추가하면 IDE가 자동으로 tlbimp.exe를 호출 RCW를 만든다 .

- RCW는 COM에 대한 프록시 역할을 함으로써 C#에서 .NET 라이브러리 사용하듯 COM API를 사용 할 수 있다 .


[ 문제 ]

- 1.COM은 메소드가 결과 반환시 object 형식으로 반환한다 . C# 코드에서는 결과를 실제 형식으로 변환해야 했다 .

- 2.COM은 오버로딩을 지원하지 않았 . 대신 메소드의 선택적 인수와 기본값을 지원한다 . c#은 4.0 버전 이하에서는 선택적 인수 /기본값 지원이 없었기에 COM API 호출시 사용하지 않을 인수를 수없이 입력해야 했다 .


[ 해결 ]

- 1번 문제는 dynamic 형식의 도입으로 해결했다 .

- 2번 문제는 선택적 인수 기본값 도입을 통해 해결했다 .

- 선택적 인수와 dynamic형식은 비주얼 스튜디오가 RCW를 만들 떄 사용하며 우리는  손대지 않는 부분이다 .


[ 코드 작 ]

- 콘솔앱 템플릿으로 새 프로젝트를 만든다

- 생성후 최상위문 사용 안함이 체크되어 있는지 / 프로젝트 속성의 전역 using 항목에서 암시적 전체사용 해제한다 .

- 종속성 우클릭 - 프로젝트 참조 추가를 선택

- COM - 형식 라이브러리 - Excel 선택

- 성공시 다음과 같이 Excel이 들어와 있음을 볼 수 있다 .

- 솔루션 탐색기 창에서 Interop.Microsoft.Office.Interop.Excel 선택후 속성창에서 Interop 형식포함을 예로 설정한다 .

using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace COMInterop
{
    class MainApp
    {
        public static void OldWay(string[,]data,string savePath)
        {
            Excel.Application excelApp = new Excel.Application();
            excelApp.Workbooks.Add(Type.Missing);
            Excel.Worksheet worksheet = (Excel.Worksheet)excelApp.ActiveSheet;

            for(int i=0;i<data.GetLength(0); i++)
            {
                ((Excel.Range)worksheet.Cells[i + 1, 1]).Value2 = data[i, 0];
                ((Excel.Range)worksheet.Cells[i + 1, 2]).Value2 = data[i, 1];
            }
            worksheet.SaveAs(savePath + "\\shpark-book-old.xlsx",
                Type.Missing,
                Type.Missing,
                Type.Missing,
                Type.Missing,
                Type.Missing,
                Type.Missing,
                Type.Missing,
                Type.Missing);
            excelApp.Quit();
        }
        public static void NewWay(string[,] data, string savePath)
        {
            Excel.Application excelApp = new Excel.Application();
            excelApp.Workbooks.Add();
            Excel.Worksheet worksheet = (Excel.Worksheet)excelApp.ActiveSheet;

            for (int i = 0; i < data.GetLength(0); i++)
            {
                worksheet.Cells[i + 1, 1]= data[i, 0];
                worksheet.Cells[i + 1, 2] = data[i, 1];
            }
            worksheet.SaveAs(savePath + "\\shpark-book-dynamic.xlsx");
            excelApp.Quit();
        }
        static void Main(string[] args)
        {
            string savePath=System.IO.Directory.GetCurrentDirectory();
            string[,] array = new string[,]
            {
                { "뇌를 자극하는 알고리즘","2009"},
                { "뇌를 자극하는 C# 4.0","2011"},
                { "뇌를 자극하는 C# 5.0","2013"},
                { "뇌를 자극하는 파이썬 3","2016"},
                { "그로킹 딥러닝","2019"},
                { "이것이 C#이다","2018"},
                { "이것이 C#이다 2E","2020"},
                { "이것이 자료구조 + 알고리즘이다 ","2022"},
                { "이것이 C#이다 3E","2023"},

            };

            Console.WriteLine("Creating Excel document in oldWay..");
            OldWay(array,savePath);
            Console.WriteLine("Creating Excel document in NewWay..");
            NewWay(array, savePath);
        }
    }
}

 

- 실행시 다음의 경로에 엑셀파일이 생성됨을 볼 수 있다 .


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

[ 동적 언어 ]

- 파이썬과 루비는 인기 동적 언어이다 .

- 동적언어는 실행시 코드를 해석해서 실행하는 방식을 사용한다 

- CLR은 IL로 컴파일 할 수 있는 언어들은 지원하지만 , 동적 언어는 지원 할 수 없었다 .


[ DLR ]

- DLR(Dynamic Language Runtime) 플랫폼으로 동적언어를 실행하게 하였다 .

- DLR은 CLR위에서 위에서 동작하며 , 그저 동적 언어를 .NET 플랫폼에서 실행 할 수 있다는 점에서

그지치 않는다 .

- DLR은 동적 언어의 코드에서 만들어진 객체에 C# VB 같은 정적 언어의 코드에서 접근하게 해준다 .

- 즉 , C#코드에서 동적언어를 실행 , 결과를 받아 볼 수 있다는 것 .


[ 호스팅 ]

- CLR 입장에서도 DLR 언어를 기반으로 구현된 동적언어라도 호스팅이 된다는 것.

- 호스팅은 주인이 손님을 섬긴다는 말 . CLR이 주인 - 동적 언어가 손님

- 파이썬에는 없지만 루비에 있는 라이브러리의 경우 C# 프로그래머는 별도 학습없이 루비 라이브러리를 이용하는 코드를 호스팅 할 수 있다 .

- CLR과 DLR 사이의 상호 운용성은 dynamic으로 해결한다 .

- 미리 형식 검사를 할 수 없는 동적 형식 언어에서 만들어진 객체는 C#의 dynamic 형식이 받을 수 있다 .


[ DLR 클래스 ]

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

- 위의 클래스는 C# 호스트 코드에서 게스트 코드를 실행시 다양한 방법으로 조합하여 사용할 수 있다 .


[ 코드 작성 ]

 

- 도구 - NuGet 패키지 관리자 - 패키지 관리자 콘솔 클릭

-Install-Package IronPython 을 콘솔에 입력한다 . Nuget 패키지가 인터넷에 접속해서 자동으로 DLR과 IronPhython을 내려받고 , 필요한 클래스 라이브러리 참조를 프로젝트에 추가해준다 .

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
namespace WithPython
{
    internal class MainApp
    {
        static void Main(string[] args)
        {
           ScriptEngine engine = Python.CreateEngine();
            ScriptScope scope = engine.CreateScope();
            scope.SetVariable("n", "박상현");
            scope.SetVariable("p", "010-123-4566");
            //파이썬 코드에서 클래스를 선언한다 .
            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)

name_card = NameCard(n, p)
name_card
");
            //파이썬 코드를 실행 , 결과를 반환한다 33행에서 NameCard(() 생성자가 호출되었으니 NameCard 객체가 생성되어 반환됨
            dynamic result =source.Execute(scope);
            result.printNameCard();//해당 객체의 메소드 호출
            Console.WriteLine("{0},{1}", result.name, result.phone);//해당 객체의 필드에 접근

        }
    }
}

- 마지막줄 dynamic 형식은 ScriptRuntime 을 이용해서 소스파일에 담긴 코드를 실행하든

scriptEngine/scriptScope/ScriptSource를 이용 ,  문자열에 담긴 코드를 실행하든 C# 코드가 호스팅 하고 있는 파이썬 코드안에서 만들어진 객체를 그대로 받아낸다 . 

- 이렇게 받아낸 파이썬 출신의 객체는 C# 코드에서 직접 메소드 호출도 가능하고 , 필드에 접근하는 것도 가능하다 .

- 이것이 dynamic 형식의 기능이다 .