다음을 통해 공유


인터페이스 - 여러 형식에 대한 동작 정의

인터페이스에는 비추상 클래스 class 또는 struct이(가) 구현해야 하는 관련 기능 그룹에 대한 정의가 포함되어 있습니다. 인터페이스는 메서드를 정의 static 할 수 있습니다. 인터페이스는 멤버에 대한 기본 구현을 정의할 수 있습니다. 인터페이스는 필드, 자동으로 구현된 속성 또는 속성과 같은 이벤트와 같은 인스턴스 데이터를 선언할 수 없습니다.

예를 들어 인터페이스를 사용하면 여러 소스의 동작을 클래스에 포함할 수 있습니다. 해당 기능은 언어가 클래스의 여러 상속을 지원하지 않기 때문에 C#에서 중요합니다. 또한 구조체는 다른 구조체나 클래스에서 실제로 상속할 수 없기 때문에 구조체에 대한 상속을 시뮬레이트하려는 경우 인터페이스를 사용해야 합니다.

다음 예제와 같이 interface 키워드를 사용하여 인터페이스를 정의합니다.

interface IEquatable<T>
{
    bool Equals(T obj);
}

인터페이스 이름은 유효한 C# 식별자 이름이어야 합니다. 규칙에 따라 인터페이스 이름은 대문자 I로 시작합니다.

IEquatable<T> 인터페이스를 구현하는 모든 클래스나 구조체에는 인터페이스에서 지정한 서명과 일치하는 Equals 메서드에 대한 정의가 포함되어 있어야 합니다. 결과적으로 T을(를) 구현하는 IEquatable<T> 유형의 클래스는 이 클래스의 인스턴스가 같은 클래스의 다른 인스턴스와 동일한지 여부를 확인할 수 있는 Equals 메서드를 포함할 수 있습니다.

IEquatable<T>의 정의에서는 Equals에 대한 구현을 제공하지 않습니다. 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있지만 클래스는 단일 클래스에서만 상속할 수 있습니다.

추상 클래스에 대한 자세한 내용은 추상 및 봉인 클래스와 클래스 멤버를 참조하세요.

인터페이스에는 인스턴스 메서드, 속성, 이벤트, 인덱서 또는 이러한 네 가지 멤버 형식의 조합이 포함될 수 있습니다. 인터페이스에는 정적 생성자, 필드, 상수 또는 연산자가 포함될 수 있습니다. 필드가 아닌 인터페이스 멤버는 다음과 같습니다 static abstract. 인터페이스에는 인스턴스 필드, 인스턴스 생성자 또는 종료자가 포함될 수 없습니다. 인터페이스 멤버는 기본적으로 공용이며, public, protected, internal, private, protected internal 또는 private protected 등의 접근성 한정자를 명시적으로 지정할 수 있습니다. private 멤버에는 기본 구현이 있어야 합니다.

암시적 구현을 사용하여 인터페이스 멤버를 구현하려면 구현 클래스의 해당 멤버가 public이고 비정적이어야 하며 인터페이스 멤버와 이름과 서명이 같아야 합니다. 명시적 인터페이스 구현을 사용하여 공용이 아닌 인터페이스 멤버를 구현해야 합니다.

참고 항목

인터페이스가 정적 멤버를 선언하는 경우 해당 인터페이스를 구현하는 형식은 동일한 서명을 가진 정적 멤버를 선언할 수도 있습니다. 이러한 멤버는 멤버를 선언하는 형식에 의해 고유하게 식별됩니다. 형식에서 선언된 정적 멤버는 인터페이스에서 선언된 정적 멤버를 재정의하지 않습니다.

인터페이스를 구현하는 클래스 또는 구조체는 인터페이스에서 제공하는 기본 구현 없이 선언된 모든 멤버에 대한 구현을 제공해야 합니다. 그러나 기본 클래스가 인터페이스를 구현하는 경우 기본 클래스에서 파생된 클래스는 해당 구현을 상속합니다.

다음 예제에서는 IEquatable<T> 인터페이스의 구현을 보여 줍니다. 구현 클래스 CarEquals 메서드의 구현을 제공해야 합니다.

public class Car : IEquatable<Car>
{
    public string? Make { get; set; }
    public string? Model { get; set; }
    public string? Year { get; set; }

    // Implementation of IEquatable<T> interface
    public bool Equals(Car? car)
    {
        return (this.Make, this.Model, this.Year) ==
            (car?.Make, car?.Model, car?.Year);
    }
}

클래스의 속성 및 인덱서는 인터페이스에 선언된 속성 또는 인덱서에 대한 추가 접근자를 정의할 수 있습니다. 예를 들어 인터페이스는 get 접근자가 있는 속성을 선언할 수 있습니다. 인터페이스를 구현하는 클래스는 getset 접근자를 둘 다 사용하는 동일한 속성을 선언할 수 있습니다. 그러나 속성 또는 인덱서에서 명시적 구현을 사용하는 경우에는 접근자가 일치해야 합니다. 명시적 구현에 대한 자세한 내용은 명시적 인터페이스 구현인터페이스 속성(C# 프로그래밍 가이드)을 참조하세요.

인터페이스는 하나 이상의 인터페이스에서 상속할 수 있습니다. 파생 인터페이스는 기본 인터페이스에서 멤버를 상속합니다. 파생 인터페이스를 구현하는 클래스는 파생 인터페이스의 기본 인터페이스 멤버 모두를 포함해 파생 인터페이스의 모든 멤버를 구현해야 합니다. 해당 클래스는 파생된 인터페이스 또는 해당 기본 인터페이스로 암시적으로 변환될 수 있습니다. 클래스는 상속하는 기본 클래스 또는 다른 인터페이스에서 상속하는 인터페이스를 통해 인터페이스를 여러 번 포함할 수 있습니다. 그러나 클래스는 인터페이스의 구현을 한 번만 제공할 수 있으며 클래스가 인터페이스를 클래스 정의의 일부로 선언하는 경우에만 제공할 수 있습니다(class ClassName : InterfaceName). 인터페이스를 구현하는 기본 클래스를 상속했기 때문에 인터페이스가 상속되는 경우 기본 클래스는 인터페이스 멤버의 구현을 제공합니다. 그러나 파생 클래스는 상속된 구현을 사용하는 대신 가상 인터페이스 멤버를 다시 구현할 수 있습니다. 인터페이스가 메서드의 기본 구현을 선언하면 해당 인터페이스를 구현하는 모든 클래스가 해당 구현을 상속합니다(인터페이스 멤버의 기본 구현에 액세스하려면 클래스 인스턴스를 인터페이스 형식으로 캐스팅해야 합니다).

또한 기본 클래스는 가상 멤버를 사용하여 인터페이스 멤버를 구현할 수 있습니다. 이 경우 파생 클래스는 가상 멤버를 재정의하여 인터페이스 동작을 변경할 수 있습니다. 가상 멤버에 대한 자세한 내용은 다형성을 참조하세요.

내부 인터페이스 작업

인터페이스 서명의 모든 형식에 공개적으로 액세스할 수 있는 한 일반적으로 공용 멤버와 함께 암시적 구현을 사용하여 내부 인터페이스를 구현할 수 있습니다. 그러나 인터페이스가 멤버 서명에서 내부 형식을 사용하는 경우 구현 클래스 멤버는 내부 형식을 노출하는 동안 공용이어야 하므로 암시적 구현이 불가능해집니다. 이러한 경우 명시적 인터페이스 구현을 사용해야 합니다.

다음 예제에서는 두 시나리오를 모두 보여 줍니다.

// Internal type that cannot be exposed publicly
internal class InternalConfiguration
{
    public string Setting { get; set; } = "";
}

// Internal interface that CAN be implemented with public members
// because it only uses public types in its signature
internal interface ILoggable
{
    void Log(string message); // string is public, so this works with implicit implementation
}

// Interface with internal accessibility using internal types
internal interface IConfigurable
{
    void Configure(InternalConfiguration config); // Internal type prevents implicit implementation
}

// This class shows both implicit and explicit interface implementation
public class ServiceImplementation : ILoggable, IConfigurable
{
    // Implicit implementation works for ILoggable because string is public
    public void Log(string message)
    {
        Console.WriteLine($"Log: {message}");
    }

    // Explicit implementation required for IConfigurable because it uses internal types
    void IConfigurable.Configure(InternalConfiguration config)
    {
        // Implementation here
        Console.WriteLine($"Configured with: {config.Setting}");
    }
    
    // If we tried implicit implementation for IConfigurable, this wouldn't compile:
    // public void Configure(InternalConfiguration config) // Error: cannot expose internal type
}

앞의 예제에서 인터페이스는 IConfigurable 메서드 서명에 내부 형식 InternalConfiguration 을 사용합니다. ServiceImplementation 메서드 시그니처에 내부 형식이 포함된 경우 허용되지 않는 메서드를 Configure 공용으로 설정해야 하므로 클래스는 암시적 구현을 사용할 수 없습니다. 대신 액세스 한정자가 없고 인터페이스 형식을 통해서만 액세스할 수 있는 명시적 인터페이스 구현이 사용됩니다.

반면, ILoggable 인터페이스 자체는 내부임에도 불구하고 해당 서명(string)의 모든 형식에 공개적으로 액세스할 수 있으므로 공용 멤버를 사용하여 인터페이스를 암시적으로 구현할 수 있습니다.

명시적 인터페이스 구현에 대한 자세한 내용은 명시적 인터페이스 구현을 참조하세요.

인터페이스 요약

인터페이스에는 다음과 같은 속성이 있습니다.

  • 8.0 이전 C# 버전에서 인터페이스는 추상 멤버만 있는 추상 기본 클래스와 유사합니다. 인터페이스를 구현하는 클래스 또는 구조체는 해당 멤버를 모두 구현해야 합니다.
  • C# 8.0부터 인터페이스는 일부 또는 모든 멤버에 대한 기본 구현을 정의할 수 있습니다. 인터페이스를 구현하는 클래스 또는 구조체는 기본 구현이 있는 멤버를 구현할 필요가 없습니다. 자세한 내용은 기본 인터페이스 메서드를 참조하세요.
  • 인터페이스는 직접 인스턴스화할 수 없습니다.
  • 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있습니다. 클래스는 기본 클래스를 상속할 수 있으며 하나 이상의 인터페이스를 제공할 수도 있습니다.