- dynamic 형식
- 덕 타이핑
- COM과 .NET 사이의 상호 운용성
- 동적 언어와의 상호 운용성
[ 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 형식의 기능이다 .
'C#' 카테고리의 다른 글
[ C# ]Conditional Attribute (0) | 2023.06.26 |
---|---|
[ C# ] foreach 문을 돌리며 리스트 안에 객체를 삭제시 주의점 (0) | 2023.04.05 |
[c#] 배열을 딕셔너리로 만들고 인덱스에 따라 정렬하여 사용하기 (0) | 2023.04.04 |
[C#]sealed 한정자 (0) | 2023.03.02 |
[ C# ] Enum 결합하여 사용하기 (0) | 2023.02.22 |