핵심 차이점 한눈에 보기
추상 메서드(Abstract Method)는 구현 내용을 제공하지 않으며, 파생 클래스가 반드시 해당 메서드를 재정의(override)하도록 강제합니다. 추상 메서드는 오직 추상 클래스 내부에서만 선언할 수 있으며, 메서드의 정의(시그니처)만 존재하고 본문은 없습니다.
가상 메서드(Virtual Method)는 추상 메서드와 달리 기본 구현을 포함하고 있으며, 추상 클래스와 일반 클래스 어디에서든 사용할 수 있습니다. 파생 클래스에는 재정의할지 여부를 선택할 수 있는 유연성을 제공합니다.
가상 함수(Virtual Function)
C#의 virtual 키워드는 메서드, 속성(property), 인덱서(indexer), 이벤트를 수정하는 데 사용됩니다. 기본 클래스에 정의된 함수를 상속받은 클래스에서 다르게 구현하고 싶다면 가상 함수를 활용하면 됩니다. 가상 함수는 각 상속 클래스마다 서로 다르게 구현될 수 있으며, 어떤 함수가 호출될지는 런타임(runtime)에 결정됩니다. 이것이 바로 다형성(polymorphism)의 핵심 원리입니다.
다음은 가상 함수를 선언하는 방법입니다.
public virtual int area() { }가상 함수 예제
using System;
namespace PolymorphismApplication {
class Shape {
protected int width, height;
public Shape(int a = 0, int b = 0) {
width = a;
height = b;
}
public virtual int area() {
Console.WriteLine("Parent class area :");
return 0;
}
}
class Rectangle : Shape {
public Rectangle(int a = 0, int b = 0) : base(a, b) {
}
public override int area() {
Console.WriteLine("Rectangle class area ");
return (width * height);
}
}
class Triangle : Shape {
public Triangle(int a = 0, int b = 0) : base(a, b) {
}
public override int area() {
Console.WriteLine("Triangle class area:");
return (width * height / 2);
}
}
class Caller {
public void CallArea(Shape sh) {
int a;
a = sh.area();
Console.WriteLine("Area: {0}", a);
}
}
class Tester {
static void Main(string[] args) {
Caller c = new Caller();
Rectangle r = new Rectangle(10, 7);
Triangle t = new Triangle(10, 5);
c.CallArea(r);
c.CallArea(t);
Console.ReadKey();
}
}
}실행 결과
Rectangle class area Area: 70 Triangle class area: Area: 25
위 예제에서 Shape 클래스의 area() 메서드는 virtual로 선언되었고, Rectangle과 Triangle 클래스는 각자의 방식으로 이를 재정의했습니다. Caller 객체가 Shape 타입의 참조로 호출했음에도 런타임에 실제 객체의 타입에 맞는 메서드가 실행되는 것을 확인할 수 있습니다.
추상 함수(Abstract Function)
C#의 abstract 키워드는 추상 클래스와 추상 함수에 사용됩니다. 추상 클래스는 추상 메서드와 비추상(일반) 메서드를 함께 포함할 수 있습니다. 추상 메서드는 본문이 없으므로, 이를 상속하는 모든 클래스는 반드시 해당 메서드를 override 키워드로 구현해야 합니다.
다음은 추상 클래스 안에서 추상 함수를 사용하는 예제입니다.
추상 함수 예제
using System;
public abstract class Vehicle {
public abstract void display();
}
public class Bus : Vehicle {
public override void display() {
Console.WriteLine("Bus");
}
}
public class Car : Vehicle {
public override void display() {
Console.WriteLine("Car");
}
}
public class Motorcycle : Vehicle {
public override void display() {
Console.WriteLine("Motorcycle");
}
}
public class MyClass {
public static void Main() {
Vehicle v;
v = new Bus();
v.display();
v = new Car();
v.display();
v = new Motorcycle();
v.display();
}
}실행 결과
Bus Car Motorcycle
Vehicle 추상 클래스는 display()라는 추상 메서드만 정의하고 있으며, 이를 상속한 Bus, Car, Motorcycle 클래스는 각각 반드시 display()를 구현해야 합니다. 부모 클래스 타입의 변수 하나로 서로 다른 자식 객체들을 다룰 수 있다는 점에서, 추상 함수 역시 다형성을 구현하는 강력한 도구입니다.
정리: 언제 무엇을 사용해야 할까?
- 기본 동작이 필요하다면 → 가상 함수: 부모 클래스의 기본 구현을 제공하면서도, 필요한 경우 자식 클래스가 선택적으로 재정의하게 하고 싶을 때 적합합니다.
- 구현을 반드시 강제해야 한다면 → 추상 함수: 공통 규약(계약)만 정의하고, 자식 클래스가 반드시 자신만의 구현을 갖도록 강제하고 싶을 때 적합합니다.