`interface` 키워드

interface 키워드 (C# 참조)

인터페이스는 계약(contract)을 정의해요. 그 계약을 구현하는 class, record, struct는 인터페이스에 정의된 멤버의 구현을 반드시 제공해야 해요.

인터페이스는 멤버에 기본 구현을 정의할 수 있고, 공통 기능을 한 번만 구현하기 위한 static 멤버도 정의할 수 있어요.

인터페이스는 static abstract 또는 static virtual 멤버를 정의해서, 구현하는 타입이 선언된 멤버를 반드시 제공해야 한다고 요구할 수 있어요. 보통 static virtual 메서드는 구현체가 연산자 오버로드 집합을 정의해야 한다는 선언이에요.

다음 예시에서 클래스 ImplementationClass는 매개 변수가 없고 void를 반환하는 SampleMethod라는 메서드를 구현해야 해요.

:::code language="csharp" source="./snippets/keywordsTypes.cs" id="14":::

더 자세한 내용과 예시는 Interfaces를 참고해요.

Access modifiers

인터페이스는 네임스페이스나 클래스의 멤버가 될 수 있어요. 네임스페이스에 선언하되 다른 타입에 중첩되지 않는 최상위 인터페이스는 public 또는 internal로 선언할 수 있고, 기본값은 internal이에요. 다른 타입 안에 중첩된 인터페이스 선언은 어떤 액세스 한정자로든 선언할 수 있어요.

구현이 없는 인터페이스 멤버(추상 멤버)는 암시적으로 public이고 다른 액세스 한정자를 가질 수 없어요. 기본 구현이 있는 인터페이스 멤버는 액세스 한정자를 지정하지 않으면 기본적으로 private이지만, public, private, protected, internal 중 어떤 한정자로든 선언할 수 있어요.

Interface members

인터페이스 선언에는 다음 멤버를 포함할 수 있어요.

Default interface members

멤버 선언은 보통 본문을 포함하지 않아요. 하지만 인터페이스 멤버가 본문을 선언할 수도 있는데, 인터페이스 안의 멤버 본문이 바로 기본 구현(default implementation) 이에요. 본문이 있는 멤버를 사용하면, 인터페이스가 이를 재정의하는 구현을 제공하지 않는 클래스와 구조체를 위해 기본 구현을 제공할 수 있어요.

[!IMPORTANT] 기본 인터페이스 멤버를 추가하면, 인터페이스를 구현하는 ref struct는 그 멤버를 명시적으로 선언해야 해요.

Static abstract and virtual members

인터페이스는 필드를 제외한 모든 멤버 유형에 대해 static abstractstatic virtual 멤버를 선언할 수 있어요. 이 멤버들을 선언하면 구현 타입이 연산자나 다른 정적 멤버를 정의하도록 요구할 수 있어요. 이 기능 덕분에 제네릭 알고리즘이 숫자와 같은 동작을 지정할 수 있죠. 예시는 .NET 런타임의 숫자 타입, 가령 xref:System.Numerics.INumber`1?displayProperty=nameWithType에서 볼 수 있어요. 이 인터페이스들은 여러 숫자 타입이 구현하는 공통 수학 연산자를 정의해요. 컴파일러는 static virtualstatic abstract 메서드에 대한 호출을 컴파일 타임에 해결해야 해요. 인터페이스에 선언된 static virtualstatic abstract 메서드에는 클래스에 선언된 virtual이나 abstract 메서드 같은 런타임 디스패치 메커니즘이 없어요. 대신 컴파일러가 컴파일 타임에 사용 가능한 타입 정보를 사용해요. 그렇기 때문에 static virtual 메서드는 거의 전적으로 제네릭 인터페이스에 선언돼요. 게다가 static virtual 또는 static abstract 메서드를 선언하는 대부분의 인터페이스는 타입 매개 변수 중 하나가 선언된 인터페이스를 구현해야 한다고 선언해요. 예를 들어 INumber<T> 인터페이스는 TINumber<T>를 구현해야 한다고 선언해요. 컴파일러는 타입 인수를 사용해 인터페이스 선언에 지정된 메서드와 연산자에 대한 호출을 해결해요. 예를 들어 int 타입은 INumber<int>를 구현해요. 타입 매개 변수 T가 타입 인수 int를 가리키면 int에 선언된 static 멤버가 호출돼요. 반대로 double이 타입 인수면 double 타입에 선언된 static 멤버가 호출되죠.

[!IMPORTANT] 인터페이스에 선언된 static abstractstatic virtual 메서드의 메서드 디스패치는 식의 컴파일 타임 타입을 사용해 결정돼요. 식의 런타임 타입이 컴파일 타임 타입과 다른 타입에서 파생된 경우에는 기본(컴파일 타임) 타입의 정적 메서드가 호출돼요.

이 기능을 직접 시험해 보려면 인터페이스의 정적 추상 멤버 튜토리얼을 따라 해 봐요.

Interface inheritance

인터페이스는 인스턴스 상태를 포함할 수 없어요. 정적 필드는 이제 허용되지만, 인스턴스 필드는 인터페이스에 허용되지 않아요. 인스턴스 자동 구현 속성은 인터페이스에서 지원되지 않아요. 그런 속성은 숨겨진 필드를 암시적으로 선언하게 되기 때문이에요. 이 규칙은 속성 선언에 미묘한 영향을 주는데, 인터페이스 선언에서 다음 코드는 classstruct에서처럼 자동 구현 속성을 선언하지 않아요. 대신 기본 구현이 없고 인터페이스를 구현하는 모든 타입에서 구현해야 하는 속성을 선언해요.

public interface INamed
{
  public string Name {get; set;}
}

인터페이스는 하나 이상의 기본 인터페이스에서 상속받을 수 있어요. 인터페이스가 다른 인터페이스에서 상속받을 때, 파생 인터페이스를 구현하는 타입은 파생 인터페이스에 선언된 멤버에 더해 기본 인터페이스의 모든 멤버도 구현해야 해요. 다음 코드에서 볼 수 있죠.

:::code language="csharp" source="./snippets/DefineTypes.cs" id="SnippetDerivedInterfaces":::

인터페이스가 기본 인터페이스에 구현된 메서드를 재정의할 때는 명시적 인터페이스 구현 구문을 사용해야 해요.

기본 타입 목록에 기본 클래스와 인터페이스가 함께 있으면, 목록에서 기본 클래스가 먼저 와야 해요.

인터페이스를 구현하는 클래스는 해당 인터페이스의 멤버를 명시적으로 구현할 수 있어요. 명시적으로 구현된 멤버는 클래스 인스턴스를 통해서는 접근할 수 없고 인터페이스의 인스턴스를 통해서만 접근할 수 있어요. 또한 기본 인터페이스 멤버는 인터페이스의 인스턴스를 통해서만 접근할 수 있어요.

명시적 인터페이스 구현에 대한 자세한 내용은 Explicit Interface Implementation을 참고해요.

Example interface implementation

다음 예시는 인터페이스 구현을 보여줘요. 이 예시에서 인터페이스는 속성 선언을, 클래스는 그 구현을 포함해요. IPoint를 구현하는 클래스의 인스턴스는 모두 정수 속성 xy를 가져요.

:::code language="csharp" source="./snippets/DefineTypes.cs" id="Snippet15":::

C# language specification

더 자세한 내용은 C# 언어 사양Interfaces 섹션을 참고해요. 여기에는 Interface operatorsInterface static constructors가 포함돼요.

더 알아보기

출처: interface keyword (C# Reference)