컴파일 vs 런타임 다형성
Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 25번째.
다형성(Polymorphism)은 "여러 가지 형태"를 의미하며, 동일한 코드가 문맥에 따라 다르게 동작하는 능력을 말합니다. C#은 두 가지 뚜렷한 유형의 다형성을 지원하며, 각각이 언제 결정(resolved)되는지 이해하면 더 유연한 코드를 작성하는 데 도움이 됩니다.
컴파일 타임 다형성(정적 다형성이라고도 함)은 프로그램이 실행되기 전에 컴파일러에 의해 결정됩니다. 메서드 오버로딩이 대표적인 예로, 여러 메서드가 이름은 같지만 매개변수가 다른 경우를 말합니다:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // 컴파일러가 int 버전을 선택합니다
calc.Add(5.0, 3.0); // 컴파일러가 double 버전을 선택합니다컴파일러는 인자 타입을 검사하고 컴파일 시점에 올바른 메서드를 선택합니다. 이 결정은 프로그램이 실행되기 전에 확정됩니다.
런타임 다형성(동적 다형성)은 프로그램이 실행되는 동안 결정됩니다. 이는 virtual 및 override 메서드에서 발생하며, 객체의 실제 타입에 따라 어떤 메서드가 실행될지가 결정됩니다:
Animal pet = new Dog(); // Animal로 선언되었지만, 실제로는 Dog임
pet.Speak(); // 런타임에 Dog의 Speak()를 호출함컴파일러는 어떤 Speak()가 실행될지 알지 못합니다 - 그 결정은 객체의 실제 타입을 기반으로 런타임에 이루어집니다. 이것이 상속 기반의 다형성을 유연하고 확장 가능한 시스템을 구축하는 데 매우 강력하게 만드는 이유입니다.
챌린지
쉬움두 가지 유형의 다형성이 실제로 작동하는 모습을 보여주는 변환기 시스템을 구축해 보겠습니다. 메서드 오버로딩(컴파일 타임 다형성)을 사용하는 클래스와 가상/재정의(virtual/override) 메서드를 사용하는 상속 계층 구조(런타임 다형성)를 함께 만들게 됩니다.
코드는 네 개의 파일로 구성됩니다:
Converter.cs:Conversion네임스페이스에Converter클래스를 정의합니다. 이 클래스는 메서드 오버로딩을 통해 컴파일 타임 다형성을 보여줍니다. 세 개의 오버로드된Convert메서드를 만드세요:Convert(int value)는"Integer: {value}"를 반환합니다.Convert(double value)는"Double: {value}"를 반환합니다.Convert(string value)는"String: {value}"를 반환합니다.
Shape.cs:Conversion네임스페이스에 기본Shape클래스를 정의합니다. 이 클래스는Name속성(string)과 이를 설정하는 생성자를 가집니다."This is a {Name}"을 반환하는Describe()라는virtual메서드를 포함하세요.Circle.cs:Shape를 상속받는Circle클래스를Conversion네임스페이스에 정의합니다.Radius속성(double)을 추가하세요. 생성자는 반지름을 인자로 받아 기본 생성자에"Circle"을 전달합니다.Describe()를 오버라이드하여"This is a Circle with radius {Radius}"를 반환하도록 합니다.Program.cs: 메인 파일에서 두 가지 유형의 다형성을 모두 시연합니다. 먼저Converter를 생성하고 입력 값으로 세 개의 오버로드된 메서드를 모두 호출합니다. 그런 다음Circle을 생성하여Shape변수에 저장하고,Describe()를 호출하여 런타임 다형성을 보여줍니다.
네 개의 입력을 받게 됩니다:
- 정수 값 하나
- 실수(double) 값 하나
- 문자열 값 하나
- 원 반지름(double) 하나
출력은 다음 형식으로 인쇄하세요:
Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}예를 들어, 입력이 42, 3.14, Hello, 5.5인 경우 출력은 다음과 같아야 합니다:
Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5주요 차이점에 주목하세요: 컴파일러는 컴파일 타임에 인자 유형에 따라 호출할 Convert 메서드를 결정하는 반면, Describe() 메서드는 Shape 변수에 저장되어 있더라도 런타임에 실제 객체 유형에 따라 결정됩니다!
치트 시트
다형성(Polymorphism)은 "많은 형태"를 의미하며, 동일한 코드가 문맥에 따라 다르게 동작하는 능력을 말합니다.
컴파일 타임 다형성(정적 다형성)은 프로그램이 실행되기 전에 컴파일러에 의해 결정됩니다. 메서드 오버로딩이 대표적인 예입니다:
public class Calculator
{
public int Add(int a, int b) => a + b;
public double Add(double a, double b) => a + b;
}
var calc = new Calculator();
calc.Add(5, 3); // 컴파일러가 int 버전을 선택합니다.
calc.Add(5.0, 3.0); // 컴파일러가 double 버전을 선택합니다.컴파일러는 인수의 타입을 검사하고 컴파일 타임에 올바른 메서드를 선택합니다.
런타임 다형성(동적 다형성)은 프로그램이 실행되는 동안 virtual 및 override 메서드를 사용하여 결정됩니다:
Animal pet = new Dog(); // Animal로 선언되었지만, 실제로는 Dog입니다.
pet.Speak(); // 런타임에 Dog의 Speak()를 호출합니다.선언된 변수 타입이 아니라, 객체의 실제 타입에 따라 런타임에 어떤 메서드가 실행될지가 결정됩니다.
직접 해보기
using System;
using Conversion;
class Program
{
public static void Main(string[] args)
{
// 입력 읽기
int intValue = Convert.ToInt32(Console.ReadLine());
double doubleValue = Convert.ToDouble(Console.ReadLine());
string stringValue = Console.ReadLine();
double radius = Convert.ToDouble(Console.ReadLine());
// TODO: 컴파일 타임 다형성(Compile-time Polymorphism) 시연
// Converter 객체를 생성하고 오버로드된 세 개의 Convert 메서드를 모두 호출
Console.WriteLine("Compile-time Polymorphism:");
// intValue, doubleValue, stringValue를 사용하여 Convert 호출
// TODO: 런타임 다형성(Runtime Polymorphism) 시연
// Circle 객체를 생성하고 Shape 변수에 저장
// 그런 다음 Shape 변수에서 Describe() 호출
Console.WriteLine("Runtime Polymorphism:");
// Circle을 생성하고 Describe() 호출
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.