18개 인터페이스
18.1 일반
인터페이스는 계약을 정의합니다. 인터페이스를 구현하는 클래스 또는 구조체는 해당 계약을 준수해야 합니다. 인터페이스는 여러 기본 인터페이스에서 상속할 수 있으며 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있습니다.
인터페이스에는 메서드, 속성, 이벤트 및 인덱서가 포함될 수 있습니다. 인터페이스 자체는 선언하는 멤버에 대한 구현을 제공하지 않습니다. 인터페이스는 인터페이스를 구현하는 클래스 또는 구조체에서 제공해야 하는 멤버만 지정합니다.
18.2 인터페이스 선언
18.2.1 일반
interface_declaration 새 인터페이스 형식을 선언하는 type_declaration(§14.7)입니다.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
interface_declaration 선택적 특성 집합(§22)과 선택적 interface_modifier집합(§18.2.2), 선택적 부분 한정자(§15.2.7), 인터페이스 이름을 지정하는 키워드 interface
및 식별자, 선택적 variant_type_parameter_list 사양(§18.2.3) 및 선택적 interface_base 다음으로 구성됩니다. 사양(§18.2.4), 선택적 type_parameter_constraints_clause사양(§15.2.5), interface_body(§18.3) 뒤에 세미콜론이 옵니다.
인터페이스 선언은 variant_type_parameter_list 제공하지 않는 한 type_parameter_constraints_clause 제공하지 않습니다.
variant_type_parameter_list 제공하는 인터페이스 선언은 제네릭 인터페이스 선언입니다. 또한 제네릭 클래스 선언 또는 제네릭 구조체 선언 내에 중첩된 모든 인터페이스는 그 자체가 제네릭 인터페이스 선언이므로 포함된 형식에 대한 형식 인수를 제공하여 생성된 형식(§8.4)을 만들어야 합니다.
18.2.2 인터페이스 한정자
interface_declaration 필요에 따라 인터페이스 한정자 시퀀스를 포함할 수 있습니다.
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier(§23.2)는 안전하지 않은 코드(§23)에서만 사용할 수 있습니다.
동일한 한정자가 인터페이스 선언에 여러 번 표시되는 것은 컴파일 시간 오류입니다.
new
한정자는 클래스 내에 정의된 인터페이스에서만 허용됩니다. 이 인터페이스는 §15.3.5에 설명된 대로 동일한 이름으로 상속된 멤버를 숨기게 지정합니다.
public
, protected
, internal
및 private
한정자는 인터페이스의 접근성을 제어합니다. 인터페이스 선언이 발생하는 컨텍스트에 따라 이러한 한정자 중 일부만 허용될 수 있습니다(§7.5.2). 부분 형식 선언(§15.2.7)에 접근성 사양(, , public
및 protected
한정자를 통해internal
)이 포함된 경우 §15.2.2private
적용됩니다.
18.2.3 Variant 형식 매개 변수 목록
18.2.3.1 일반
Variant 형식 매개 변수 목록은 인터페이스 및 대리자 형식에서만 발생할 수 있습니다. 일반 type_parameter_list차이는 각 형식 매개 변수에 대한 선택적 variance_annotation .
variant_type_parameter_list
: '<' variant_type_parameters '>'
;
variant_type_parameters
: attributes? variance_annotation? type_parameter
| variant_type_parameters ',' attributes? variance_annotation?
type_parameter
;
variance_annotation
: 'in'
| 'out'
;
분산 주석이out
면 형식 매개 변수는 공변이라고 합니다. 분산 주석이in
면 형식 매개 변수는 반공변이라고 합니다. 분산 주석이 없으면 형식 매개 변수는 고정이라고 합니다.
예: 다음에서:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
X
는 공변성이고 반Y
공변성이며Z
고정입니다.끝 예제
제네릭 인터페이스가 여러 부분(§15.2.3)으로 선언된 경우 각 부분 선언은 각 형식 매개 변수에 대해 동일한 분산을 지정해야 합니다.
18.2.3.2 분산 안전성
형식의 형식 매개 변수 목록에서 분산 주석이 발생하면 형식 선언 내에서 형식이 발생할 수 있는 위치가 제한됩니다.
다음 중 하나가 있는 경우 T 형식은 출력 안전하지 않습니다 .
-
T
은 반공변 형식 매개 변수입니다. -
T
는 출력 안전하지 않은 요소 형식을 가진 배열 형식입니다. -
T
는 다음 중 하나Sᵢ,... Aₑ
이상이 보유하는 제네릭 형식에서 생성된 인터페이스 또는 대리자 형식S<Xᵢ, ... Xₑ>
Aᵢ
입니다.-
Xᵢ
가 공변성이거나 고정Aᵢ
적이며 출력에 안전하지 않습니다. -
Xᵢ
는 반공변성이거나 고정Aᵢ
적이며 입력 안전하지 않습니다.
-
다음 중 하나가 있는 경우 T 형식은 입력 안전하지 않습니다 .
-
T
는 공변 형식 매개 변수입니다. -
T
은 입력 안전하지 않은 요소 형식의 배열 형식입니다. -
T
는 다음 중 하나S<Aᵢ,... Aₑ>
이상이 보유하는 제네릭 형식에서 생성된 인터페이스 또는 대리자 형식S<Xᵢ, ... Xₑ>
Aᵢ
입니다.-
Xᵢ
가 공변성이거나 고정Aᵢ
적이며 입력 안전하지 않습니다. -
Xᵢ
는 반공변성이거나 고정Aᵢ
적이며 출력에 안전하지 않습니다.
-
직관적으로 출력 위치에서는 출력 안전하지 않은 형식이 금지되고 입력 위치에서는 입력 안전하지 않은 형식이 금지됩니다.
형식은 출력 안전하지 않은 경우 출력에 안전 하며 입력 안전하지 않은 경우 입력이 안전 합니다.
18.2.3.3 분산 변환
분산 주석의 목적은 인터페이스 및 대리자 형식에 대한 보다 관대하지만 형식이 안전한 변환을 제공하는 것입니다. 이를 위해 암시적(§10.2) 및 명시적 변환(§10.3)의 정의는 다음과 같이 정의된 분산 변환성 개념을 사용합니다.
형식은 변형 형식 T<Aᵢ, ..., Aᵥ>
T<Bᵢ, ..., Bᵥ>
매개 변수로 선언된 인터페이스 또는 대리자 형식인 경우 T
형식으로 분산 변환할 수 있으며 각 변형 형식 매개 변수 T<Xᵢ, ..., Xᵥ>
Xᵢ
에 대해 다음 중 하나가 유지됩니다.
-
Xᵢ
가 공변이고 암시적 참조 또는 ID 변환이 에서Aᵢ
Bᵢ
-
Xᵢ
가 반공변이고 암시적 참조 또는 ID 변환이 에서Bᵢ
Aᵢ
-
Xᵢ
가 고정되고 ID 변환이 에서Aᵢ
Bᵢ
18.2.4 기본 인터페이스
인터페이스는 인터페이스의 명시적 기본 인터페이스라고 하는 0개 이상의 인터페이스 형식에서 상속할 수 있습니다 . 인터페이스에 하나 이상의 명시적 기본 인터페이스가 있는 경우 해당 인터페이스의 선언에서 인터페이스 식별자 뒤에 콜론과 기본 인터페이스 형식의 쉼표로 구분된 목록이 표시됩니다.
interface_base
: ':' interface_type_list
;
명시적 기본 인터페이스는 인터페이스 형식(§8.4, §18.2)을 생성할 수 있습니다. 기본 인터페이스는 범위에 있는 형식 매개 변수를 포함할 수 있지만 자체적으로 형식 매개 변수가 될 수 없습니다.
생성된 인터페이스 형식의 경우 명시적 기본 인터페이스는 제네릭 형식 선언에서 명시적 기본 인터페이스 선언을 사용하고 기본 인터페이스 선언의 각 type_parameter 대해 생성된 형식의 해당 type_argument 대체하여 형성됩니다.
인터페이스의 명시적 기본 인터페이스는 최소한 인터페이스 자체(§7.5.5)만큼 액세스할 수 있어야 합니다.
참고: 예를 들어 인터페이스의 interface_base
private
를internal
지정 하는 것은 컴파일 시간 오류입니다public
. 끝 메모
인터페이스가 직접 또는 간접적으로 자체적으로 상속하는 것은 컴파일 시간 오류입니다.
인터페이스의 기본 인터페이스는 명시적 기본 인터페이스 및 기본 인터페이스입니다. 즉, 기본 인터페이스 집합은 명시적 기본 인터페이스, 명시적 기본 인터페이스 등의 완전한 전이적 닫기입니다. 인터페이스는 기본 인터페이스의 모든 멤버를 상속합니다.
예제: 다음 코드에서
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}
의 기본 인터페이스는
IComboBox
IControl
,ITextBox
및IListBox
. 즉, 위의 인터페이스는IComboBox
멤버SetText
및SetItems
Paint
.을 상속합니다.끝 예제
생성된 제네릭 형식에서 상속된 멤버는 형식 대체 후에 상속됩니다. 즉, 멤버의 모든 구성 요소 형식에는 기본 클래스 선언의 형식 매개 변수가 class_base 사양에 사용되는 해당 형식 인수로 대체됩니다.
예제: 다음 코드에서
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }
인터페이스
IDerived
는 형식 매개 변수Combine
가T
.로 대체된 후 메서드를string[,]
상속합니다.끝 예제
또한 인터페이스를 구현하는 클래스 또는 구조체는 인터페이스의 모든 기본 인터페이스를 암시적으로 구현합니다.
부분 인터페이스 선언(§15.2.7)의 여러 부분에 대한 인터페이스 처리는 §15.2.4.3에서 자세히 설명합니다.
인터페이스의 모든 기본 인터페이스는 출력이 안전해야 합니다(§18.2.3.2).
18.3 인터페이스 본문
인터페이스의 interface_body 인터페이스의 멤버를 정의합니다.
interface_body
: '{' interface_member_declaration* '}'
;
18.4 인터페이스 멤버
18.4.1 일반
인터페이스의 멤버는 기본 인터페이스에서 상속된 멤버 및 인터페이스 자체에 의해 선언된 멤버입니다.
interface_member_declaration
: interface_method_declaration
| interface_property_declaration
| interface_event_declaration
| interface_indexer_declaration
;
인터페이스 선언은 0개 이상의 멤버를 선언합니다. 인터페이스의 멤버는 메서드, 속성, 이벤트 또는 인덱서여야 합니다. 인터페이스에는 상수, 필드, 연산자, 인스턴스 생성자, 종료자 또는 형식을 포함할 수 없으며 인터페이스에는 모든 종류의 정적 멤버가 포함될 수 없습니다.
모든 인터페이스 멤버는 암시적으로 공용 액세스 권한을 갖습니다. 인터페이스 멤버 선언에 한정자를 포함하는 것은 컴파일 시간 오류입니다.
interface_declaration 새 선언 공간(§7.3)을 만들고 interface_declaration 포함된 형식 매개 변수 및 interface_member_declaration 이 선언 공간에 새 멤버를 소개합니다. 다음 규칙은 interface_member_declaration적용됩니다.
- 인터페이스 선언의 variant_type_parameter_list 형식 매개 변수의 이름은 동일한 variant_type_parameter_list 다른 모든 형식 매개 변수의 이름과 달라야 하며 인터페이스의 모든 멤버 이름과 다릅니다.
- 메서드의 이름은 동일한 인터페이스에 선언된 모든 속성 및 이벤트의 이름과 다릅니다. 또한 메서드의 서명(§7.6)은 동일한 인터페이스에 선언된 다른 모든 메서드의 서명과 달라야 하며, 동일한 인터페이스에 선언된 두 메서드에는 단독으로
in
out
ref
다른 시그니처가 없습니다. - 속성 또는 이벤트의 이름은 동일한 인터페이스에 선언된 다른 모든 멤버의 이름과 다릅니다.
- 인덱서의 서명은 동일한 인터페이스에 선언된 다른 모든 인덱서의 서명과 다릅니다.
인터페이스의 상속된 멤버는 특히 인터페이스의 선언 공간에 속하지 않습니다. 따라서 인터페이스는 상속된 멤버와 동일한 이름 또는 서명을 가진 멤버를 선언할 수 있습니다. 이 경우 파생된 인터페이스 멤버는 기본 인터페이스 멤버를 숨깁니다 . 상속된 멤버를 숨기는 것은 오류로 간주되지 않지만 컴파일러에서 경고를 발생시킵니다. 경고를 표시하지 않으면 파생 인터페이스 멤버의 선언에는 파생 멤버가 기본 멤버를 숨기려는 것임을 나타내는 한정자가 포함되어 new
야 합니다. 이 항목은 §7.7.2.3에서 자세히 설명합니다.
상속된 멤버를 new
숨기지 않는 선언에 한정자가 포함된 경우 해당 효과에 대한 경고가 발생합니다. 이 경고는 한정자를 제거하여 new
표시되지 않습니다.
참고: 클래스
object
의 멤버는 엄밀히 말하면 모든 인터페이스의 멤버가 아닙니다(§18.4). 그러나 클래스object
의 멤버는 모든 인터페이스 형식(§12.5)의 멤버 조회를 통해 사용할 수 있습니다. 끝 메모
여러 부분으로 선언된 인터페이스의 멤버 집합(§15.2.7)은 각 파트에서 선언된 멤버의 합합입니다. 인터페이스 선언의 모든 부분의 본문은 동일한 선언 공간(§7.3)을 공유하고 각 멤버의 범위(§7.7)는 모든 부분의 본문으로 확장됩니다.
18.4.2 인터페이스 메서드
인터페이스 메서드는 interface_method_declaration사용하여 선언됩니다.
interface_method_declaration
: attributes? 'new'? return_type interface_method_header
| attributes? 'new'? ref_kind ref_return_type interface_method_header
;
interface_method_header
: identifier '(' parameter_list? ')' ';'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause* ';'
;
인터페이스 메서드 선언의 특성, return_type, ref_return_type, 식별자 및 parameter_list 클래스의 메서드 선언과 동일한 의미를 갖습니다(§15.6). 인터페이스 메서드 선언은 메서드 본문을 지정할 수 없으므로 선언은 항상 세미콜론으로 끝납니다.
인터페이스 메서드의 모든 매개 변수 형식은 입력 안전(§18.2.3.2)이어야 하며 반환 형식은 void
출력이 안전해야 합니다. 또한 출력 또는 참조 매개 변수 형식도 출력에 안전해야 합니다.
참고: 출력 매개 변수는 일반적인 구현 제한으로 인해 입력이 안전해야 합니다. 끝 메모
또한 메서드의 모든 형식 매개 변수에 대한 각 클래스 형식 제약 조건, 인터페이스 형식 제약 조건 및 형식 매개 변수 제약 조건은 입력로부터 안전해야 합니다.
또한 메서드의 모든 형식 매개 변수에 대한 각 클래스 형식 제약 조건, 인터페이스 형식 제약 조건 및 형식 매개 변수 제약 조건은 입력로부터 안전해야 합니다.
이러한 규칙은 인터페이스의 공변 또는 반공변 사용이 형식 안전 상태로 유지되도록 합니다.
예제:
interface I<out T> { void M<U>() where U : T; // Error }
형식 매개 변수 제약
T
조건으로 사용하는U
것이 입력로부터 안전하지 않기 때문에 형식이 잘못되었습니다.이 제한 사항이 없는 경우 다음과 같은 방식으로 형식 안전을 위반할 수 있습니다.
class B {} class D : B {} class E : B {} class C : I<D> { public void M<U>() {...} } ... I<B> b = new C(); b.M<E>();
이것은 실제로 호출입니다
C.M<E>
. 그러나 이 호출은E
파생D
되어야 하므로 형식 안전성이 여기에 위반됩니다.끝 예제
18.4.3 인터페이스 속성
인터페이스 속성은 interface_property_declaration사용하여 선언됩니다.
interface_property_declaration
: attributes? 'new'? type identifier '{' interface_accessors '}'
| attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}'
;
interface_accessors
: attributes? 'get' ';'
| attributes? 'set' ';'
| attributes? 'get' ';' attributes? 'set' ';'
| attributes? 'set' ';' attributes? 'get' ';'
;
ref_interface_accessor
: attributes? 'get' ';'
;
인터페이스 속성 선언의 특성, 형식 및 식별자는 클래스의 속성 선언과 동일한 의미를 갖습니다(§15.7).
인터페이스 속성 선언의 접근자는 클래스 속성 선언(§15.7.3)의 접근자에 해당합니다. 단 , accessor_body 항상 세미콜론이어야 합니다. 따라서 접근자가 속성이 읽기/쓰기, 읽기 전용 또는 쓰기 전용인지를 나타내기만 하면 됩니다.
인터페이스 속성의 형식은 get 접근자가 있는 경우 출력이 안전하며 set 접근자가 있는 경우 입력이 안전해야 합니다.
18.4.4 인터페이스 이벤트
인터페이스 이벤트는 interface_event_declaration사용하여 선언됩니다.
interface_event_declaration
: attributes? 'new'? 'event' type identifier ';'
;
인터페이스 이벤트 선언의 특성, 형식 및 식별자는 클래스의 이벤트 선언과 동일한 의미를 갖습니다(§15.8).
인터페이스 이벤트의 형식은 입력이 안전해야 합니다.
18.4.5 인터페이스 인덱서
인터페이스 인덱서는 interface_indexer_declaration사용하여 선언됩니다.
interface_indexer_declaration
: attributes? 'new'? type 'this' '[' parameter_list ']'
'{' interface_accessors '}'
| attributes? 'new'? ref_kind type 'this' '[' parameter_list ']'
'{' ref_interface_accessor '}'
;
인터페이스 인덱서 선언의 특성, 형식 및 parameter_list 클래스의 인덱서 선언과 동일한 의미를 갖습니다(§15.9).
인터페이스 인덱서 선언의 접근자는 클래스 인덱서 선언(§15.9)의 접근자에 해당합니다. 단 , accessor_body 항상 세미콜론이어야 합니다. 따라서 접근자는 단순히 인덱서가 읽기/쓰기, 읽기 전용 또는 쓰기 전용인지를 나타냅니다.
인터페이스 인덱서의 모든 매개 변수 형식은 입력이 안전해야 합니다(§18.2.3.2). 또한 출력 또는 참조 매개 변수 형식도 출력에 안전해야 합니다.
참고: 출력 매개 변수는 일반적인 구현 제한으로 인해 입력이 안전해야 합니다. 끝 메모
인터페이스 인덱서의 형식은 get 접근자가 있는 경우 출력에 안전하며 set 접근자가 있는 경우 입력이 안전해야 합니다.
18.4.6 인터페이스 멤버 액세스
인터페이스 멤버 액세스(§12.8.7) 및 인덱서 액세스(§12.8.12.3) 형식 I.M
및 I[A]
식을 통해 액세스합니다. 여기서 I
인터페이스 형식입니다. M
해당 인터페이스 형식의 메서드, 속성 또는 이벤트이며 A
인덱서 인수 목록입니다.
엄격하게 단일 상속(상속 체인의 각 인터페이스에 정확히 0개 또는 1개의 직접 기본 인터페이스가 있음)인 인터페이스의 경우, 멤버 조회(§12.5), 메서드 호출(§12.8.10.2), 및 인덱서 액세스(§12.8.12.3) 규칙의 효과는 클래스 및 구조체의 경우와 정확히 동일합니다. 더 파생된 멤버는 이름이나 서명이 동일한 덜 파생된 멤버를 숨깁니다. 그러나 다중 상속 인터페이스의 경우 관련이 없는 두 개 이상의 기본 인터페이스가 동일한 이름 또는 서명으로 멤버를 선언할 때 모호성이 발생할 수 있습니다. 이 하위 클래스는 몇 가지 예를 보여 줍니다. 그 중 일부는 모호성으로 이어지며 다른 예제는 그렇지 않습니다. 모든 경우에 명시적 캐스트를 사용하여 모호성을 해결할 수 있습니다.
예제: 다음 코드에서
interface IList { int Count { get; set; } } interface ICounter { void Count(int i); } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count(1); // Error x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count(1); // Ok, invokes ICounter.Count } }
처음 두 문은 in의 멤버 조회(§12.5)
Count
IListCounter
가 모호하기 때문에 컴파일 시간 오류를 발생합니다. 예제에서 설명한 것처럼 모호성은 적절한 기본 인터페이스 형식으로 캐스팅x
하여 확인됩니다. 이러한 캐스트에는 런타임 비용이 없습니다. 이는 단지 컴파일 타임에 인스턴스를 덜 파생된 형식으로 보는 것으로 구성됩니다.끝 예제
예제: 다음 코드에서
interface IInteger { void Add(int i); } interface IDouble { void Add(double d); } interface INumber : IInteger, IDouble {} class C { void Test(INumber n) { n.Add(1); // Invokes IInteger.Add n.Add(1.0); // Only IDouble.Add is applicable ((IInteger)n).Add(1); // Only IInteger.Add is a candidate ((IDouble)n).Add(1); // Only IDouble.Add is a candidate } }
n.Add(1)
호출은 §12.6.4IInteger.Add
오버로드 확인 규칙을 적용하여 선택합니다. 마찬가지로 호출n.Add(1.0)
에서 선택합니다.IDouble.Add
명시적 캐스트를 삽입하면 후보 메서드가 하나만 있으므로 모호성이 없습니다.끝 예제
예제: 다음 코드에서
interface IBase { void F(int i); } interface ILeft : IBase { new void F(int i); } interface IRight : IBase { void G(); } interface IDerived : ILeft, IRight {} class A { void Test(IDerived d) { d.F(1); // Invokes ILeft.F ((IBase)d).F(1); // Invokes IBase.F ((ILeft)d).F(1); // Invokes ILeft.F ((IRight)d).F(1); // Invokes IBase.F } }
멤버가
IBase.F
멤버에 의해 숨겨집니다ILeft.F
. 따라서 호출d.F(1)
이 선택됩니다ILeft.F
. 하지만IBase.F
이 호출은 을 통과하는IRight
액세스 경로에 숨겨지지 않은 것처럼 보입니다.다중 상속 인터페이스에 숨기는 직관적인 규칙은 다음과 같습니다. 멤버가 액세스 경로에 숨겨지면 모든 액세스 경로에 숨겨집니다. 액세스 경로가
IDerived
ILeft
숨겨IBase
지므로IBase.F
멤버는 액세스 경로에서IDerived
숨겨집니다IRight
IBase
.끝 예제
18.5 정규화된 인터페이스 멤버 이름
인터페이스 멤버는 정규화된 인터페이스 멤버 이름으로도 참조됩니다. 인터페이스 멤버의 정규화된 이름은 멤버가 선언된 인터페이스의 이름과 점, 멤버의 이름으로 구성됩니다. 멤버의 정규화된 이름은 멤버가 선언된 인터페이스를 참조합니다.
예제: 지정된 선언
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }
정규화된 이름
Paint
이며IControl.Paint
SetText의 정규화된 이름은 .입니다ITextBox.SetText
. 위의 예제에서는 .로Paint
참조ITextBox.Paint
할 수 없습니다.끝 예제
인터페이스가 네임스페이스의 일부인 경우 정규화된 인터페이스 멤버 이름에는 네임스페이스 이름이 포함될 수 있습니다.
예제:
namespace System { public interface ICloneable { object Clone(); } }
네임스페이
System
스 내에서 메서드의 정규화된 인터페이스 멤버 이름ICloneable.Clone
입니다System.ICloneable.Clone
Clone
.끝 예제
18.6 인터페이스 구현
18.6.1 일반
인터페이스는 클래스 및 구조체에 의해 구현될 수 있습니다. 클래스 또는 구조체가 인터페이스를 직접 구현함을 나타내기 위해 인터페이스는 클래스 또는 구조체의 기본 클래스 목록에 포함됩니다.
예제:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }
끝 예제
인터페이스를 직접 구현하는 클래스 또는 구조체도 모든 인터페이스의 기본 인터페이스를 암시적으로 구현합니다. 클래스 또는 구조체가 기본 클래스 목록의 모든 기본 인터페이스를 명시적으로 나열하지 않는 경우에도 마찬가지입니다.
예제:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }
여기서 클래스
TextBox
는 둘 다IControl
구현합니다ITextBox
.끝 예제
클래스 C
가 인터페이스를 직접 구현하는 경우 파생 C
된 모든 클래스도 인터페이스를 암시적으로 구현합니다.
클래스 선언에 지정된 기본 인터페이스는 인터페이스 형식(§8.4, §18.2)을 생성할 수 있습니다.
예제: 다음 코드는 클래스가 생성된 인터페이스 형식을 구현하는 방법을 보여 줍니다.
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}
끝 예제
제네릭 클래스 선언의 기본 인터페이스는 §18.6.3에 설명된 고유성 규칙을 충족해야 합니다.
18.6.2 명시적 인터페이스 멤버 구현
인터페이스를 구현하기 위해 클래스 또는 구조체는 명시적 인터페이스 멤버 구현을 선언 할 수 있습니다. 명시적 인터페이스 멤버 구현은 정규화된 인터페이스 멤버 이름을 참조하는 메서드, 속성, 이벤트 또는 인덱서 선언입니다.
예제:
interface IList<T> { T[] GetElements(); } interface IDictionary<K, V> { V this[K key] { get; } void Add(K key, V value); } class List<T> : IList<T>, IDictionary<int, T> { public T[] GetElements() {...} T IDictionary<int, T>.this[int index] {...} void IDictionary<int, T>.Add(int index, T value) {...} }
IDictionary<int,T>.this
명시적 인터페이스 멤버 구현은 다음과 같습니다IDictionary<int,T>.Add
.끝 예제
예: 경우에 따라 인터페이스 멤버의 이름이 구현 클래스에 적합하지 않을 수 있습니다. 이 경우 인터페이스 멤버는 명시적 인터페이스 멤버 구현을 사용하여 구현될 수 있습니다. 예를 들어 파일 추상화 구현 클래스는 파일 리소스를 해제하는 효과가 있는 멤버 함수를 구현
Close
하고 명시적 인터페이스 멤버 구현을 사용하여 인터페이스의Dispose
메서드를 구현IDisposable
할 수 있습니다.interface IDisposable { void Dispose(); } class MyFile : IDisposable { void IDisposable.Dispose() => Close(); public void Close() { // Do what's necessary to close the file System.GC.SuppressFinalize(this); } }
끝 예제
메서드 호출, 속성 액세스, 이벤트 액세스 또는 인덱서 액세스에서 정규화된 인터페이스 멤버 이름을 통해 명시적 인터페이스 멤버 구현에 액세스할 수 없습니다. 명시적 인터페이스 멤버 구현은 인터페이스 인스턴스를 통해서만 액세스할 수 있으며, 이 경우 멤버 이름으로만 참조됩니다.
명시적 인터페이스 멤버 구현에 다른 한정자(§15.6) extern
를 포함하는 것은 컴파일 시간 오류입니다 async
.
명시적 인터페이스 메서드 구현에 type_parameter_constraints_clause포함하는 컴파일 시간 오류입니다. 제네릭 명시적 인터페이스 메서드 구현에 대한 제약 조건은 인터페이스 메서드에서 상속됩니다.
참고: 명시적 인터페이스 멤버 구현에는 다른 멤버와 다른 접근성 특성이 있습니다. 명시적 인터페이스 멤버 구현은 메서드 호출 또는 속성 액세스에서 정규화된 인터페이스 멤버 이름을 통해 액세스할 수 없으므로 전용입니다. 그러나 인터페이스를 통해 액세스할 수 있으므로 선언된 인터페이스처럼 공용이라는 의미도 있습니다. 명시적 인터페이스 멤버 구현은 다음 두 가지 주요 용도로 사용됩니다.
- 명시적 인터페이스 멤버 구현은 클래스 또는 구조체 인스턴스를 통해 액세스할 수 없으므로 클래스 또는 구조체의 공용 인터페이스에서 인터페이스 구현을 제외할 수 있습니다. 이는 클래스 또는 구조체가 해당 클래스 또는 구조체의 소비자에게 관심이 없는 내부 인터페이스를 구현할 때 특히 유용합니다.
- 명시적 인터페이스 멤버 구현을 사용하면 동일한 서명을 가진 인터페이스 멤버를 명확하게 구분할 수 있습니다. 명시적 인터페이스 멤버 구현이 없으면 클래스 또는 구조체가 동일한 서명 및 반환 형식을 가진 인터페이스 멤버의 다른 구현을 가질 수 없습니다. 클래스 또는 구조체가 동일한 시그니처를 사용하지만 반환 형식이 다른 모든 인터페이스 멤버에서 구현하는 것은 불가능합니다.
끝 메모
명시적 인터페이스 멤버 구현이 유효하려면 클래스 또는 구조체는 정규화된 인터페이스 멤버 이름, 형식, 형식 매개 변수 수 및 매개 변수 형식이 명시적 인터페이스 멤버 구현과 정확히 일치하는 멤버를 포함하는 기본 클래스 목록의 인터페이스 이름을 지정해야 합니다. 인터페이스 함수 멤버에 매개 변수 배열이 있는 경우 연결된 명시적 인터페이스 멤버 구현의 해당 매개 변수는 한정자를 가질 params
수 있지만 필수는 아닙니다. 인터페이스 함수 멤버에 매개 변수 배열이 없는 경우 연결된 명시적 인터페이스 멤버 구현에는 매개 변수 배열이 없습니다.
예: 따라서 다음 클래스에서
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }
IComparable.CompareTo
의 기본 클래스 목록에IComparable
나열되지 않고 기본 인터페이스Shape
가 아니므로 컴파일 시간 오류가ICloneable
발생합니다. 마찬가지로 선언에서class Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }
에 대한 선언
ICloneable.Clone
Ellipse
은 기본 클래스 목록에ICloneable
명시적으로 나열되지 않으므로 컴파일 시간 오류가Ellipse
발생합니다.끝 예제
명시적 인터페이스 멤버 구현의 정규화된 인터페이스 멤버 이름은 멤버가 선언된 인터페이스를 참조해야 합니다.
예: 따라서 선언에서
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }
Paint의 명시적 인터페이스 멤버 구현은 .가 아니라
IControl.Paint
로ITextBox.Paint
작성해야 합니다.끝 예제
18.6.3 구현된 인터페이스의 고유성
제네릭 형식 선언에 의해 구현된 인터페이스는 가능한 모든 생성 형식에 대해 고유하게 유지됩니다. 이 규칙이 없으면 생성된 특정 형식을 호출하는 올바른 메서드를 결정하는 것은 불가능합니다.
예: 제네릭 클래스 선언을 다음과 같이 작성할 수 있다고 가정합니다.
interface I<T> { void F(); } class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict { void I<U>.F() {...} void I<V>.F() {...} }
허용된 경우 다음 경우에 실행할 코드를 결정하는 것은 불가능합니다.
I<int> x = new X<int, int>(); x.F();
끝 예제
제네릭 형식 선언의 인터페이스 목록이 유효한지 확인하려면 다음 단계를 수행합니다.
- 제네릭 클래스, 구조체 또는 인터페이스 선언
L
에 직접 지정된 인터페이스 목록을 만듭니C
다. -
L
에 이미 있는 인터페이스의 기본 인터페이스에L
추가합니다. - 에서
L
중복 항목을 제거합니다. - 생성된
C
가능한 생성 형식이 형식 인수로L
대체된 후 두 인터페이스L
가 동일해지면 선언C
이 유효하지 않습니다. 가능한 생성 형식을 모두 결정할 때 제약 조건 선언은 고려되지 않습니다.
참고: 위의 클래스 선언
X
에서 인터페이스 목록은L
다음과 같이 구성됩니다l<U>
I<V>
. 형식이 동일하고U
생성된 형식V
이 있으면 이러한 두 인터페이스가 동일한 형식이 되므로 선언이 유효하지 않습니다. 끝 메모
다른 상속 수준에서 지정된 인터페이스가 통합될 수 있습니다.
interface I<T>
{
void F();
}
class Base<U> : I<U>
{
void I<U>.F() {...}
}
class Derived<U, V> : Base<U>, I<V> // Ok
{
void I<V>.F() {...}
}
이 코드는 둘 다 Derived<U,V>
구현하더라도 유효 I<U>
합니다I<V>
. 코드
I<int> x = new Derived<int, int>();
x.F();
효과적으로 다시 구현하기 때문에 Derived
(§18.6.7)에서 Derived<int,int>'
메서드를 호출합니다I<int>
.
18.6.4 제네릭 메서드 구현
제네릭 메서드가 인터페이스 메서드를 암시적으로 구현하는 경우 각 메서드 형식 매개 변수에 지정된 제약 조건은 두 선언(인터페이스 형식 매개 변수가 적절한 형식 인수로 대체된 후)에서 동일해야 합니다. 여기서 메서드 형식 매개 변수는 왼쪽에서 오른쪽으로 서수 위치로 식별됩니다.
예: 다음 코드에서:
interface I<X, Y, Z> { void F<T>(T t) where T : X; void G<T>(T t) where T : Y; void H<T>(T t) where T : Z; } class C : I<object, C, string> { public void F<T>(T t) {...} // Ok public void G<T>(T t) where T : C {...} // Ok public void H<T>(T t) where T : string {...} // Error }
메서드
C.F<T>
는 암시적으로 구현합니다.I<object,C,string>.F<T>
이 경우C.F<T>
모든 형식 매개 변수에 대한 암시적 제약 조건이므로 제약 조건을T: object
object
지정할 필요(또는 허용되지 않음)는 아닙니다. 인터페이스 형식 매개 변수가C.G<T>
해당 형식 인수로 대체된 후 제약 조건이 인터페이스의 제약 조건과 일치하기 때문에 메서드I<object,C,string>.G<T>
는 암시적으로 구현합니다. 봉인된 형식(C.H<T>
이 경우)을 제약 조건으로 사용할 수 없기 때문에 메서드string
에 대한 제약 조건은 오류입니다. 암시적 인터페이스 메서드 구현의 제약 조건이 일치해야 하기 때문에 제약 조건을 생략하면 오류가 발생합니다. 따라서 암시적으로 구현I<object,C,string>.H<T>
할 수 없습니다. 이 인터페이스 메서드는 명시적 인터페이스 멤버 구현을 사용하여 구현할 수 있습니다.class C : I<object, C, string> { ... public void H<U>(U u) where U : class {...} void I<object, C, string>.H<T>(T t) { string s = t; // Ok H<T>(t); } }
이 경우 명시적 인터페이스 멤버 구현은 제약 조건이 엄격하게 약한 공용 메서드를 호출합니다. t에서 s로의 할당은 소스 코드에서 이 제약 조건을 표현할 수 없더라도 제약 조건을
T
상속하므로 유효T: string
합니다. 끝 예제
참고: 제네릭 메서드가 인터페이스 메서드를 명시적으로 구현하는 경우 구현 메서드에 제약 조건이 허용되지 않습니다(§15.7.1, §18.6.2). 끝 메모
18.6.5 인터페이스 매핑
클래스 또는 구조체는 클래스 또는 구조체의 기본 클래스 목록에 나열된 인터페이스의 모든 멤버에 대한 구현을 제공해야 합니다. 구현 클래스 또는 구조체에서 인터페이스 멤버의 구현을 찾는 프로세스를 인터페이스 매핑이라고 합니다.
클래스 또는 구조체 C
에 대한 인터페이스 매핑은 기본 클래스 목록에 C
지정된 각 인터페이스의 각 멤버에 대한 구현을 찾습니다. 멤버 I.M
가 선언되는 인터페이스인 특정 인터페이스 멤버 I
M
의 구현은 각 클래스 또는 구조S
체를 검사하여 일치 항목이 위치할 때까지 각 연속 기본 클래스C
에서 C
시작하여 반복하여 결정됩니다.
- 일치하는
S
I
명시적 인터페이스 멤버 구현의 선언을 포함하는 경우M
이 멤버는 .의I.M
구현입니다. - 그렇지 않은 경우 일치하는
S
비정적 공용 멤버의 선언이 포함된 경우M
이 멤버는 구현입니다I.M
. 둘 이상의 멤버가 일치하는 경우 어떤 멤버가 구현I.M
되는지 지정되지 않습니다. 이 상황은 제네릭 형식에 선언된 두 멤버의 서명이 다르지만 형식 인수가 해당 서명을 동일하게 만드는 생성된 형식인 경우에만S
발생할 수 있습니다.
기본 클래스 목록에 C
지정된 모든 인터페이스의 모든 멤버에 대해 구현을 배치할 수 없는 경우 컴파일 시간 오류가 발생합니다. 인터페이스의 멤버에는 기본 인터페이스에서 상속된 멤버가 포함됩니다.
생성된 인터페이스 형식의 멤버는 §15.3.3에 지정된 대로 형식 매개 변수를 해당 형식 인수로 대체한 것으로 간주됩니다.
예: 예를 들어 제네릭 인터페이스 선언을 지정합니다.
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }
생성된 인터페이스
I<string[]>
에는 멤버가 있습니다.string[] F(int x, string[,][] y); string[] this[int y] { get; }
끝 예제
인터페이스 매핑을 위해 클래스 또는 구조체 멤버 A
는 다음과 같은 경우 인터페이스 멤버 B
와 일치합니다.
-
A
및B
메서드이며 이름, 형식 및 매개 변수 목록은A
동일합니다B
. -
A
및B
속성, 이름 및 형식A
이B
동일하며A
동일한 접근자를B
갖습니다(A
명시적 인터페이스 멤버 구현이 아닌 경우 추가 접근자를 포함하도록 허용됨). -
A
은B
이벤트이며 이름과 형식A
B
은 동일합니다. -
A
B
은 인덱서이고 형식 및 매개 변수 목록이A
B
동일하며A
동일한 접근자를B
갖습니다(A
명시적 인터페이스 멤버 구현이 아닌 경우 추가 접근자를 포함하도록 허용됨).
인터페이스 매핑 알고리즘의 주목할 만한 의미는 다음과 같습니다.
- 명시적 인터페이스 멤버 구현은 인터페이스 멤버를 구현하는 클래스 또는 구조체 멤버를 결정할 때 동일한 클래스 또는 구조체의 다른 멤버보다 우선합니다.
- 비공용 멤버나 정적 멤버 모두 인터페이스 매핑에 참여하지 않습니다.
예제: 다음 코드에서
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }
ICloneable.Clone
명시적 인터페이스 멤버 구현이 다른 멤버C
보다 우선하기 때문에 'ICloneable'의Clone
멤버가 구현됩니다.끝 예제
클래스 또는 구조체가 이름, 형식 및 매개 변수 형식이 같은 멤버를 포함하는 둘 이상의 인터페이스를 구현하는 경우 각 인터페이스 멤버를 단일 클래스 또는 구조체 멤버에 매핑할 수 있습니다.
예제:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }
여기서는
Paint
두 메서드의 메서드를 모두IControl
IForm/ Paint
>의Page
메서드에 매핑합니다. 물론 두 메서드에 대해 별도의 명시적 인터페이스 멤버 구현이 있을 수도 있습니다.끝 예제
클래스 또는 구조체가 숨겨진 멤버를 포함하는 인터페이스를 구현하는 경우 일부 멤버는 명시적 인터페이스 멤버 구현을 통해 구현해야 할 수 있습니다.
예제:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }
이 인터페이스의 구현에는 하나 이상의 명시적 인터페이스 멤버 구현이 필요하며 다음 양식 중 하나를 사용합니다.
class C1 : IDerived { int IBase.P { get; } int IDerived.P() {...} } class C2 : IDerived { public int P { get; } int IDerived.P() {...} } class C3 : IDerived { int IBase.P { get; } public int P() {...} }
끝 예제
클래스가 동일한 기본 인터페이스를 가진 여러 인터페이스를 구현하는 경우 기본 인터페이스의 구현이 하나만 있을 수 있습니다.
예제: 다음 코드에서
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } class ComboBox : IControl, ITextBox, IListBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} void IListBox.SetItems(string[] items) {...} }
기본 클래스 목록의 명명된 이름,
IControl
상속된 클래스 및 상속된 클래스에IControl
대해ITextBox
별도의 구현을IControl
IListBox
사용할 수 없습니다. 실제로 이러한 인터페이스에 대한 별도의 ID는 없습니다. 대신, 동일한 구현을ITextBox
구현하고IListBox
공유하며IControl
, 단순히 3개의 인터페이스,ComboBox
IControl
및ITextBox
3개의 인터페이스를 구현하는 것으로IListBox
간주됩니다.끝 예제
기본 클래스의 멤버는 인터페이스 매핑에 참여합니다.
예제: 다음 코드에서
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }
의 구현
F
에Class1
Class2's
사용되는 메서드Interface1
입니다.끝 예제
18.6.6 인터페이스 구현 상속
클래스는 기본 클래스에서 제공하는 모든 인터페이스 구현을 상속합니다.
인터페이스를 명시적으로 다시 구현하지 않으면 파생 클래스는 기본 클래스에서 상속하는 인터페이스 매핑을 어떤 방식으로도 변경할 수 없습니다.
예: 선언에서
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }
메서드는 메서드를 숨기지만 매핑을
Paint
변경하지TextBox
않으며 클래스 인스턴스 및 인터페이스 인스턴스를Paint
통해 호출하면 다음과 같은 효과가 발생합니다.Control
Control.Paint
IControl.Paint
Paint
Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint();
끝 예제
그러나 인터페이스 메서드가 클래스의 가상 메서드에 매핑되는 경우 파생 클래스가 가상 메서드를 재정의하고 인터페이스의 구현을 변경할 수 있습니다.
예: 위의 선언을 다음으로 다시 작성
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }
이제 다음 효과가 관찰됩니다.
Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes TextBox.Paint();
끝 예제
명시적 인터페이스 멤버 구현은 가상으로 선언할 수 없으므로 명시적 인터페이스 멤버 구현을 재정의할 수 없습니다. 그러나 명시적 인터페이스 멤버 구현이 다른 메서드를 호출하는 것은 완벽하게 유효하며, 파생 클래스에서 재정의할 수 있도록 다른 메서드를 가상으로 선언할 수 있습니다.
예제:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }
여기서 파생된
Control
클래스는 메서드를 재정의IControl.Paint
하여 구현을 특수화할PaintControl
수 있습니다.끝 예제
18.6.7 인터페이스 다시 구현
인터페이스 구현을 상속하는 클래스는 기본 클래스 목록에 인터페이스를 포함하여 인터페이스를 다시 구현할 수 있습니다.
인터페이스의 다시 구현은 인터페이스의 초기 구현과 정확히 동일한 인터페이스 매핑 규칙을 따릅니다. 따라서 상속된 인터페이스 매핑은 인터페이스의 다시 구현을 위해 설정된 인터페이스 매핑에 아무런 영향을 주지 않습니다.
예: 선언에서
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }
에 매핑
Control
IControl.Paint
된다는Control.IControl.Paint
사실은 에 매핑MyControl
IControl.Paint
되는 다시 구현에MyControl.Paint
영향을 주지 않습니다.끝 예제
상속된 공용 멤버 선언 및 상속된 명시적 인터페이스 멤버 선언은 다시 구현된 인터페이스에 대한 인터페이스 매핑 프로세스에 참여합니다.
예제:
interface IMethods { void F(); void G(); void H(); void I(); } class Base : IMethods { void IMethods.F() {} void IMethods.G() {} public void H() {} public void I() {} } class Derived : Base, IMethods { public void F() {} void IMethods.H() {} }
여기서의 구현
IMethods
은 인터페이스 메서드를 ,Derived
및Derived.F
Base.IMethods.G
.에Derived.IMethods.H
매핑Base.I
합니다.끝 예제
클래스가 인터페이스를 구현하는 경우 해당 인터페이스의 모든 기본 인터페이스도 암시적으로 구현합니다. 마찬가지로 인터페이스의 다시 구현은 모든 인터페이스의 기본 인터페이스를 암시적으로 다시 구현하는 것입니다.
예제:
interface IBase { void F(); } interface IDerived : IBase { void G(); } class C : IDerived { void IBase.F() {...} void IDerived.G() {...} } class D : C, IDerived { public void F() {...} public void G() {...} }
여기서는 다시 구현하여
IDerived
매핑합니다IBase
IBase.F
D.F
.끝 예제
18.6.8 추상 클래스 및 인터페이스
비 추상 클래스와 마찬가지로 추상 클래스는 클래스의 기본 클래스 목록에 나열된 인터페이스의 모든 멤버에 대한 구현을 제공해야 합니다. 그러나 추상 클래스는 인터페이스 메서드를 추상 메서드에 매핑할 수 있습니다.
예제:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }
여기서 맵
IMethods
및F
추상 메서드에 대한G
구현은 파생되는 비 추상 클래스에서C
재정의되어야 합니다.끝 예제
명시적 인터페이스 멤버 구현은 추상적일 수 없지만 명시적 인터페이스 멤버 구현은 물론 추상 메서드를 호출할 수 있습니다.
예제:
interface IMethods { void F(); void G(); } abstract class C: IMethods { void IMethods.F() { FF(); } void IMethods.G() { GG(); } protected abstract void FF(); protected abstract void GG(); }
여기서 파생
C
되는 비 추상 클래스는 재정FF
의GG
하여IMethods
실제 구현을 제공해야 합니다.끝 예제
ECMA C# draft specification