컴파일 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 변수에 저장되어 있더라도 런타임에 실제 객체 유형에 따라 결정됩니다!
직접 해보기
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() 호출
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.