런타임에 형식 검사가 이루어지는 데이터 형식
dynamic
을 제외한 C#의 모든 데이터 형식은 컴파일 단계에서 형식 검사 수행dynamic
키워드를 만나면 형식 검사를 프로그램 실행 때로 미룸class MyClass
{
public void FuncAAA()
{ // Do Nothing }
}
class MainApp
{
static void Main(string[] args)
{
dynamic obj = new MyClass();
obj.FuncAAA();
obj.FuncBBB();
/* FuncBBB() 메소드는 선언되지 않았지만 obj가 dynamic으로 선언되어 컴파일러 형식 검사를 피해 간다.
만약 dynamic으로 선언되지 않았다면 컴파일 에러가 발생한다. */
}
}
dynamic
형식으로 선언하면 형식 검사를 런타임 때로 미룬다는 점 이용예제
using System;
namespace DuckTyping
{
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("Robot.Walk"); }
public void Swim()
{ Console.WriteLine("Robot.Swim"); }
public void Quack()
{ Console.WriteLine("Robot.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();
/* Duck, Mallard, Robot 클래스가
Walk(), Swim(), Quack() 메소드를 구현하고 있으므로
이 코드는 컴파일도 실행도 문제없이 잘 됨
덕 타이핑 관점에서 보면 해당 코드에서 선언한
Duck도 오리이고 Robot도 오리이다. */
}
}
}
}
컴포넌트(Component)
- 프로그래밍에 있어 재사용 및 교체가 가능한 각각의 독립된 모듈
- 위 그림에서 확인할 수 있듯이 컴포넌트 기반 프로그래밍을 하면 마치 레고 블록처럼 이미 만들어진 컴포넌들을 조합하여 화면을 구성할 수 있다.
- 장난감에 배터리를 교체할 때 고려해야 할 것은 장난감 단자와 배터리 단자가 맞물려야 한다는 것이다. 그래야 비로소 제 기능을 발휘하고 이 때야 비로소 배터리는 일종의 '장치'가 될 수 있다. 많은 하드웨어 제품의 경우 각각 독립된 기능을 가진 모듈(부품, 자동차를 생각하면 이해하기 쉽다)로 만들어져 이들을 조합하여 제품을 만들고 문제가 발생할 경우 이를 유발하는 모듈을 교체하여 문제를 해결한다. 이렇게 하드웨어처럼 독립적인 기능을 수행하고 추후에 교환될 수 있도록 소프트웨어를 만들기 위해 나온 기술이 컴포넌트 기술이다.
- 객체지향 언어에 컴포넌트 개념 도입
- 장난감과 배터리는 각각 독립적으로 문제없이 돌아간다고 가정할 때 상호간의 단자만 규격(표준으로 삼아 따르도록 정해 놓은 수치나 형식)에 맞을 시 어떠한 배터리를 교환해도 장난감은 정상 작동할 것이다. 이러한 규격화된 단자의 개념을 소프트웨어에 적용한 것이 객체지향 언어의 인터페이스와 메소드이다. 인터페이스는 사용자에게 소프트웨어를 쓰기 위한 메소드(=장치)를 공개하고, 규격화된 메소드 환경에서 소프트웨어를 개발할 수 있는 환경을 제공해준다. 컴포넌트 개념을 잘 적용한 소프트웨어란 부품(인터페이스를 구현받는 클래스)만 바꾸어주었을 시 오류없이 잘 작동되는 것을 의미한다.
- 컴포넌트의 은닉성
- 컴포넌트는 인터페이스를 통해서만 접근할 수 있으며 컴포넌트 내의 정보는 외부로부터 모두 숨겨진다. 즉 컴포넌트를 사용하기 위해서는 어떤 인터페이스를 사용해야 하는지만을 알 수 있다.
- 컴포넌트와 클래스, 객체는 다른 개념이다.
[Reference] https://hanamon.kr/%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-component%EB%9E%80/
COM(Component Object Model)
- Component 규약을 따르는 Microsoft사가 주체가 되어 만든 컴포넌트 표준 규격
- Component 규약이란 컴파일러의 종류에 관계없이 이에 따라 제작한 Component는 어떤 컴파일러에서도 그 파일을 사용할 수 있도록 하는 규약이다.
- Component 규약에 따라 제작한 Component는 어떤 컴파일러에서도 그 파일을 사용할 수 있도록하는 규약이다.
[Reference]
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=sunflowerove&logNo=60025990287
.NET의 RCW(Runtime Callable Wrapper)를 통한 COM 사용
C#과 COM 사이의 상호 운용성을 저하시킨 원인
개선된 선택적 인수와 dynamic 형식은 비주얼 스튜디오가 RCW를 만들 때 사용하여 사용자는 전혀 손대지 않아도 된다.
예제
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" }
};
Console.WriteLine("Creating Excel document in old way...");
OldWay(array, savePath);
Console.WriteLine("Creating Excel document in new way...");
NewWay(array, savePath);
}
}
}
동적 언어 지원
호스팅(hosting)
- 서버컴퓨터의 전체 또는 일정 공간을 이용할 수 있도록 임대해 주는 서비스
- 주인(host)이 손님을 섬긴다는 뜻
- CLR=주인, 동적 언어=손님
동적 언어 호스팅을 위한 C# 코드 클래스
게스트 코드 실행 방법
ScriptRuntime
ScriptRuntime runtime = Python.CreateRuntime();
dynamic result = runtime.ExecuteFile("namecard.py");
ScriptEngine
, ScriptScope
, ScriptSource
예제
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
namespace WithPython
{
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
NameCard(n, p)
");
// ScriptSource method : 파이썬 코드 실행하여 결과 반환
dynamic result = source.Execute(scope);
result.printNameCard(); // 객체 메소드 호출 가능
Console.WriteLine("{0}, {1}", result.name, result.phone); // 필드 접근 가능
}
}
}