C#에서 이해하기 쉬운 다형성 개념

다형성은 동일한 인터페이스를 통해 다양한 형태로 동작할 수 있는 능력을 의미합니다. 객체 지향 프로그래밍에서는 이를 "하나의 인터페이스, 여러 구현"으로 요약할 수 있습니다.

다형성은 정적 다형성과 동적 다형성으로 나뉩니다. 정적 다형성은 컴파일 시에 함수 호출이 결정되며, 동적 다형성은 실행 시에 실제 메서드가 선택됩니다.

정적 다형성

정적 다형성은 컴파일 단계에서 메서드와 객체 간의 연결을 처리하는 방식으로, 이른바 '정적 바인딩'이라고도 합니다. C#에서는 다음 두 가지 기법을 통해 정적 다형성을 실현합니다:

  • 메서드 오버로딩 (함수 중복 정의)
  • 연산자 오버로딩

연산자 오버로딩은 별도의 주제로 다루기 때문에, 여기서는 메서드 오버로딩에 초점을 맞춥니다.

메서드 오버로딩

같은 이름의 메서드를 같은 범위 내에서 여러 번 정의할 수 있으며, 각 메서드는 매개변수의 수나 타입이 달라야 합니다. 반환 타입만 다른 경우는 오버로딩으로 인정되지 않습니다.

아래 예제는 display라는 이름의 메서드를 다양한 데이터 유형에 대해 사용하는 방법을 보여줍니다:

using System;

namespace PolymorphismExample {
    class DataPrinter {
        public void Display(int value) {
            Console.WriteLine("정수 출력: {0}", value);
        }

        public void Display(double value) {
            Console.WriteLine("실수 출력: {0}", value);
        }

        public void Display(string text) {
            Console.WriteLine("문자열 출력: {0}", text);
        }

        static void Main(string[] args) {
            DataPrinter printer = new DataPrinter();
            printer.Display(42);           // 정수
            printer.Display(3.14159);      // 실수
            printer.Display("안녕하세요");  // 문자열
            Console.ReadKey();
        }
    }
}

실행 결과는 다음과 같습니다:

정수 출력: 42
실수 출력: 3.14159
문자열 출력: 안녕하세요

동적 다형성

동적 다형성은 런타임 시에 적절한 메서드가 호출되는 방식입니다. 이를 위해 abstract 키워드를 사용해 추상 클래스를 정의할 수 있습니다. 추상 클래스는 일부 메서드의 구현을 제공하지 않으며, 파생 클래스에서 반드시 구현해야 합니다.

추상 클래스의 특징:

  • 추상 클래스는 인스턴스화할 수 없습니다.
  • 추상 클래스 외부에서 추상 메서드를 선언할 수 없습니다.
  • sealed 키워드로 클래스를 최종화하면 상속이 불가능하며, 추상 클래스는 sealed로 선언될 수 없습니다.

다음은 추상 클래스의 예제입니다:

using System;

namespace ShapeDemo {
    abstract class Figure {
        public abstract double CalculateArea();
    }

    class Rectangle : Figure {
        private double length;
        private double width;

        public Rectangle(double a = 0, double b = 0) {
            length = a;
            width = b;
        }

        public override double CalculateArea() {
            Console.WriteLine("사각형의 면적 계산 중...");
            return length * width;
        }
    }

    class Program {
        static void Main(string[] args) {
            Rectangle rect = new Rectangle(8, 6);
            double area = rect.CalculateArea();
            Console.WriteLine("면적: {0}", area);
            Console.ReadKey();
        }
    }
}

출력 결과:

사각형의 면적 계산 중...
면적: 48

또한, virtual 키워드를 사용해 가상 메서드를 정의하면, 파생 클래스에서 해당 메서드를 재정의할 수 있습니다. 이는 런타임에 호출될 메서드를 결정하는 데 활용됩니다.

다음은 가상 메서드를 활용한 동적 다형성 예제입니다:

using System;

namespace DynamicPolymorphism {
    class GeometricShape {
        protected double dimensionA;
        protected double dimensionB;

        public GeometricShape(double a = 0, double b = 0) {
            dimensionA = a;
            dimensionB = b;
        }

        public virtual double ComputeArea() {
            Console.WriteLine("기본 도형의 면적 계산");
            return 0;
        }
    }

    class Square : GeometricShape {
        public Square(double side) : base(side, side) { }

        public override double ComputeArea() {
            Console.WriteLine("정사각형의 면적 계산");
            return dimensionA * dimensionA;
        }
    }

    class RightTriangle : GeometricShape {
        public RightTriangle(double baseLen, double height) : base(baseLen, height) { }

        public override double ComputeArea() {
            Console.WriteLine("직각삼각형의 면적 계산");
            return (dimensionA * dimensionB) / 2;
        }
    }

    class AreaCalculator {
        public void ProcessShape(GeometricShape shape) {
            double result = shape.ComputeArea();
            Console.WriteLine("계산된 면적: {0}", result);
        }
    }

    class TestRunner {
        static void Main(string[] args) {
            AreaCalculator calculator = new AreaCalculator();
            Square square = new Square(5);
            RightTriangle triangle = new RightTriangle(6, 8);

            calculator.ProcessShape(square);
            calculator.ProcessShape(triangle);
            Console.ReadKey();
        }
    }
}

실행 결과:

정사각형의 면적 계산
계산된 면적: 25
직각삼각형의 면적 계산
계산된 면적: 24

추상 메서드와 가상 메서드의 차이

  • 추상 메서드는 본체 없이 선언되며, 파생 클래스에서 반드시 구현해야 합니다. 그렇지 않으면 클래스 생성이 불가능합니다.
  • 가상 메서드는 기본 구현이 있어야 하며, 파생 클래스에서 선택적으로 재정의할 수 있습니다.
  • 추상 메서드는 반드시 추상 클래스 내에서만 선언 가능하지만, 가상 메서드는 일반 클래스에서도 사용할 수 있습니다.
  • 추상 메서드는 강제적인 오버라이드를 요구하는 반면, 가상 메서드는 선택적 오버라이드입니다.

요약하면, 추상 메서드는 자식 클래스가 반드시 구현해야 하는 책임이며, 가상 메서드는 확장 가능한 기본 구현을 제공합니다. 둘 다 파생 클래스에서 재정의될 수 있으며, 런타임 다형성의 핵심 요소입니다.

태그: 다형성 가상 메서드 추상 클래스 메서드 오버로딩 C#

8월 10일 04:11에 게시됨