Skip to main content

[This is C#] Chapter 8. 인터페이스와 추상 클래스

이것이 C#이다 책을 읽고 공부한 노트입니다.




인터페이스의 선언 #

  • 인터페이스(Interface)
    • 메소드, 이벤트, 인덱서, 프로퍼티만을 가질 수 있다.
    • 인터페이스는 파생 클래스가 반드시 자신이 가진 것을 구현하도록 강제한다.
    • 접근 제한 한정자를 사용할 수 없고, 모든 것이 public으로 선언된다.
    • 인스턴스도 만들 수 없다.
    • 참조는 가능하다.
interface IInterface
{
    void Method();
}

class MyClass : IInterface
{
    public void Method()
    {
        //...
    }
}

class MainApp
{
    static void Main(string[] args)
    {
        IInterface a = new MyClass();
        a.Method();
    }
}



인터페이스를 상속하는 인터페이스 #

  • 인터페이스를 상속할 수 있는 것
    • 클래스
    • 구조체
    • 인터페이스
      • 이미 해당 인터페이스를 상속하는 클래스들이 많거나, 상속하려는 인터페이스가 소스 코드가 아닌 어셈블리로만 제공되는 경우, 인터페이스를 상속하는 인터페이스를 만들 수 있겠다.
interface IBase
{
    void BaseMethod();
}

interface IDerived : IBase // 인터페이스를 상속하는 인터페이스
{
    void DerivedMethod();
}

class MyClass : IDerived
{
    public void BaseMethod()
    {
        //...
    }

    public void DerivedMethod()
    {
        //...
    }
}

class MainApp
{
    static void Main(string[] args)
    {
        Derived d = new MyClass();
        d.BaseMethod();
        d.DerivedMethod();
    }
}



여러 개의 인터페이스, 한꺼번에 상속하기 #

  • C#은 여러 클래스를 한꺼번에 상속할 수 없다.
    • 죽음의 다이아몬드 문제 때문이다.
    • 하지만 여러 인터페이스를 상속할 순 있다.
interface IRun
{
    void Run();
}

interface IFly
{
    void Fly();
}

class FlyingCar : IRun, IFly
{
    public void Run()
    {
        Console.WriteLine("Run");
    }

    public void Fly()
    {
        Console.WriteLine("Fly");
    }
}

class MainApp
{
    static void Main(string[] args)
    {
        FlyingCar car = new FlyingCar();
        car.Run();
        car.Fly();

        IRun run = new FlyingCar();
        run.Run();

        IFly fly = new FlyingCar();
        fly.Fly();
    }
}

  • 포함(Containment) 기법
    • 다른 클래스의 기능을 새로운 클래스에 넣는 방법으로써, 물려받고 싶은 기능을 가진 클래스들을 필드로 선언해 넣는 것이다.
class MyVehicle
{
    Car car = new Car();
    Plane plane = new Plane();

    public void Run() { car.Run(); }
    public void Fly() { plane.Fly(); }
}



인터페이스의 기본 구현 메소드 #

  • 기존 인터페이스에 메소드를 추가하고 싶다.
    • 하지만 인터페이스를 상속하는 파생 클래스가 수 없이 많아서 일일이 다 수정하기에는 너무 번거롭다.
    • 그렇다면, 인터페이스에 기본 구현을 제공하는 메소드를 만들자.
interface IInterface
{
    void PreviousMethod();

    void NewMethod() // 기본 구현 메소드
    {
        Console.WriteLine("New Method");
    }
}

class Myclass : IInterface
{
    public void PreviousMethod()
    {
        Console.WriteLine("Previous Method");
    }
}


class MainApp
{
    static void Main(string[] args)
    {
        IInterface i = new Myclass();
        i.PreviousMethod();
        i.NewMethod();

        Myclass m = new Myclass();
        m.PreviousMethod();
        m.NewMethod(); // 컴파일 에러! NewMethod()를 오버라이딩 하진 않았다. 
    }
}



명시적인 인터페이스 구현 #

  • 암묵적 구현
    • public으로 구현하는 방식이다.
  • 명시적 구현
    • private으로 구현하며 [인터페이스이름.함수명] 형식으로 함수 이름을 표시한다.
public interface IMyInterface
{
    string Name { get; set; }
        int Age { get; set; }
}

public class MyClass : IMyInterface
{
        // 암시적인 구현 = public
    public string Name { get; set; }      

        // 명시적인 구현 = private = 인터페이스로 형변환 시에만 호출할 수 있다. 
    int IMyInterface.Age { get; set; } 
}

class Program
{
    static void Main(string[] args)
    {
                MyClass a = new MyClass();
        a.Name = "Kim";
        a.Age = 1;  // (X) 접근 불가 

        ((IMyInterface)a).Age = 1; // (O) 접근 가능
        }
}

  • 여러 개의 인터페이스를 상속받을 때, 이름이 같은 서로 다른 멤버를 선언하는 사례도 해결할 수 있겠다.
interface ILeft
{
    int P { get;}
}
interface IRight
{
    int P();
}

class Middle : ILeft, IRight
{
    public int P() { return 0; }
    int ILeft.P { get { return 0; } }
}



추상 클래스: 인터페이스와 클래스 사이 #

  • 추상 클래스(Abstract class)
    • 구현을 가질 수 있지만, 인스턴스를 만들 수 없다.
    • 추상 메소드를 가질 수 있다.
      • 추상 메소드는 파생 클래스가 반드시 이 메소드를 구현하도록 강제한다.
      • 추상 메소드는 반드시 public, protected, internal, protected internal 중에 하나여야 한다. private이면 안 된다.
abstract class AbstractBase
{
    public abstract void AbstractMethod();
}

class MyClass : AbstractBase
{
    public override void AbstractMethod()
    {
        //...
    }
}

  • 추상 클래스가 또 다른 추상 클래스를 상속하는 경우
abstract class AbstractBase
{
    public abstract void AbstractMethod();
}

abstract class AbstractDerived : AbstractBase
{
    public abstract override void AbstractMethod();
}

class MyClass : AbstractDerived
{
    public override void AbstractMethod()
    {
        Console.WriteLine("Hello");
    }
}

class MainApp
{
    static void Main(string[] args)
    {
        AbstractBase b = new MyClass();
        b.AbstractMethod();
    }
}