본문 바로가기
- C#/C# is 도샵

[C#] virtual abstract interface

by david_동근 2025. 5. 20.

OOP에서 상속에 관한 키워드 virtual(추상), abstract(가상) 한정자와 interface를 정리했습니다.

세가지 키워드 모두 Override(재정의) 할 수 있다는 공통점이 있지만, 사용 방식과 목적에 차이가 있습니다.

 


Virtual

메서드나 속성, 어떤 이벤트에 아래 코드처럼 virtual 키워드를 붙여 사용할 수 있습니다.

부모 클래스에서는 virtual로 선언, 자식 클래스에서는 override로 재정의합니다.

자식에서 override 하는게 필수가 아니기도 하며,

override를 사용하지 않을 경우 상속받는 기능 그대로 완전하게 수행합니다.

class Mammal {
    public virtual void Cry() {
        Console.WriteLine("Mammal Animal makes Noise and Vocalization");
    }
}

class Dog : Mammal {
    public override void Cry() {
        Console.WriteLine("Barks, 멍멍!");
        // Dog 클래스는 Cry메서드를 Override 했군요
    }
}

class Human : Mammal {
    // 암것도 없으니 암것도 굳이 Override 하지 않음
    // 부모 클래스 기능 그대로 물려 받아용
}

 

필요에 따라 자식에서 class에서 override할 수 있습니다. 고로 위 코드의 결과는 아래와 같습니다.

Mammal m1 = new Dog();
m1.Cry();  // 출력 : barks, 멍멍!

Mammal m2 = new Human();
m2.Cry();  // 출력 : Mammal Animal makes Noise and Vocalization

※ 주의

virtual 은 오직 override로만 덮어쓸 수 있습니다.

public class Dog : Mammal {
    public new void Speak() { // 이렇게 하면 override가 아님!
        Console.WriteLine("Dog says WTF");
    }
}

 

이렇게 new 키워드를 써버리면 override는 안되고 shadowing이 돼,

부모 클래스 타입으로 사용할 경우 여전히 부모의 메서드가 호출됩니다.

 

Abstract

abstract는 virtual보다 좀 더 강제성이 있는 편입니다. 자식 클래스에서 꼭 override 재정의해야합니다.

사용하려면, 클레스와 메서드 모두 abstract class / abstract void 어쩌구메서드 이어야 합니다.

안스턴트를 직접 만들 수 없으며, 상속을 통해서만 가능합니다.

(Unity용 C#으로 예제 코드를 만들어 봤어유)

public abstract class Enemy : MonoBehaviour
{
    public abstract void Attack(); // abstract 자체는 아직 기능 구현 없음
}
public class Skeleton : Enemy
{
    public override void Attack()
    {
        Debug.Log("Shoot an Arrow");
    }
}

public class Zombie : Enemy
{
    public override void Attack()
    {
        Debug.Log("zombie Punch!");
    }
}
Enemy e1 = new Skeleton(); // ㅇㅇ, 굿 인스턴트 생성
e1.Attack(); // 출력 : Shoot an Arrow

Enemy e2 = new Enemy(); // ㄴㄴ! 추상 클래스는 직접 생성 불가해유

 

abstract class는 직접 생성 불가하오니, 틀만 제공하고 자식 클래스에서 맡기는 상속 전용이라 생각하면 편합니다.

그렇기 때문에, 자식 클래스에서 (종류별로) 무조건 다르게 기능해야 할 때 사용하게됩니다.

 

Interface

구현할 기능 전혀 없이 무엇이 있어야 할지만 선언합니다.

Class에서 interface를 implement(구현)하며, 그 안의 모든 멤버를 꼭 구현해야 합니다.

interface의 가장 큰 특징은, 하나의 클래스가 여러 인터페이스를 동시에 구현할 수 있다는 점입니다.

즉, 다중 상속이 가능하며, 여러 클래스에 공통적인 기능을 추가하기 위해 사용합니다.

interface ICry
{
    void Cry();  // 구현 ㄴㄴ, 선언만해요
}
class Dog : ICry
{
    public void Cry()
    {
        Console.WriteLine("barks, 멍멍!");
    }
}

class Human : ICry
{
    public void Cry()
    {
        Console.WriteLine("T.T, 엉엉");
    }
}
ICry dog = new Dog();
dog.Cry();  // 출력 : barks, 멍멍!

 

다중 상속이기 때문에, 서로 관련 없는 클래스도 interface로 구현 가능하며,

이렇게 interface로 형성된 관계를 다형성이라고 합니다.

접근 제한자로 public 만 허용이 됩니다.

(C#8 이후부터는 default 구현이 가능해졌지만, 일반적으로는 구현 없이 선언만 작성하는 것이 관례라네용)

 

Unity에서 사용 예를 확인해보겠습니다.

using UnityEngine;
using UnityEngine.EventSystems;

public class ButtonClickHandler : MonoBehaviour, IPointerClickHandler
{
    public void OnPointerClick(PointerEventData eventData)
    {
        Debug.Log("Button clicked!");
    }
}

 

 

위와 같은 인터페이스 덕분에 Unity에서는 다양한 기능들이 컴포넌트 방식으로 확장되며 제공됩니다.

 

정리

3가지 특징 별로 표로 정리했습니다.

  virtual abstract interface
설명 기본 동작 제공 + 선택적 재정의 허용 틀은 제공하고, 구현은 자식에게 강제 어떤 기능이 필요하다는 규약(계약) 제공
기본 기능 구현 있음 (선택적 오버라이드) 없음 (구현 불가) 없음 (관례적으로 없음)
오버라이드 여부 선택 필수 필수
클래스 인스턴스화 가능 불가능 인터페이스 자체는 인스턴스화 불가
상속/구현 대상 클래스만 클래스만 클래스 & 구조체
다중 상속/구현 불가능 불가능 가능
키워드 virtual, override abstract, override interface, 구현 시 : 사용
기본 동작 정의 가능 불가능 불가능 (C#8부터 일부 가능 but ㄴ)
유지보수 성격 확장 가능성 중시 구조 강제 중심 계약 기반 설계 중심

 


 

갑자기 헷갈려서 정리하게 되었습니다.

그럼 오늘도 좋은 하루 되시길 바라요~ *⋆꒰ঌ(⁎ᴗ͈ˬᴗ͈⁎)໒꒱⋆*