19.1 일반
인터페이스는 계약을 정의합니다. 인터페이스를 구현하는 클래스 또는 구조체는 해당 계약을 준수해야 합니다. 인터페이스는 여러 기본 인터페이스에서 상속할 수 있으며 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있습니다.
인터페이스는 §19.4에 설명된 대로 다양한 종류의 멤버를 포함할 수 있습니다. 인터페이스 자체는 선언하는 함수 멤버의 일부 또는 전부에 대한 구현을 제공할 수 있습니다. 인터페이스에서 구현을 제공하지 않는 멤버는 추상적입니다. 해당 구현은 인터페이스를 구현하는 클래스 또는 구조체 또는 재정의 정의를 제공하는 파생 인터페이스에서 제공해야 합니다.
참고: 지금까지 인터페이스에 새 함수 멤버를 추가하면 해당 인터페이스 형식의 모든 기존 소비자에게 영향을 미쳤습니다. 그것은 호환성이 손상되는 변화였습니다. 인터페이스 함수 멤버 구현이 추가되어 개발자는 구현자가 해당 구현을 재정의할 수 있도록 하면서 인터페이스를 업그레이드할 수 있습니다. 인터페이스 사용자는 구현을 호환성이 손상되지 않는 변경으로 수락할 수 있습니다. 그러나 요구 사항이 다른 경우 제공된 구현을 재정의할 수 있습니다. 끝 메모
19.2 인터페이스 선언
19.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선택적 특성 집합(§23)과 선택적 interface_modifier집합(§19.2.2), 선택적 부분 한정자(§15.2.7) 및 인터페이스 이름을 지정하는 키워드 interface 및 식별자로 구성됩니다. 선택적 variant_type_parameter_list 사양(§19.2.3) 뒤에 선택적 interface_base 사양(§19.2.4)이 잇습니다.), 선택적 type_parameter_constraints_clause사양(§15.2.5), interface_body(§19.3) 뒤에 세미콜론이 옵니다.
인터페이스 선언은 variant_type_parameter_list을/를 또한 제공하는 경우가 아니라면 type_parameter_constraints_clause을/를 제공하지 않습니다.
variant_type_parameter_list를 제공하는 인터페이스 선언은 제네릭 인터페이스 선언입니다. 또한 제네릭 클래스 선언 또는 제네릭 구조체 선언 내에 중첩된 모든 인터페이스는 그 자체가 제네릭 인터페이스 선언이므로 포함된 형식에 대한 형식 인수를 제공하여 생성된 형식(§8.4)을 만들어야 합니다.
19.2.2 인터페이스 한정자
interface_declaration 필요에 따라 인터페이스 한정자 시퀀스를 포함할 수 있습니다.
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2)는 안전하지 않은 코드(§24)에서만 사용할 수 있습니다.
동일한 한정자가 인터페이스 선언에 여러 번 표시되는 것은 컴파일 시간 오류입니다.
new 한정자는 클래스 내에 정의된 인터페이스에서만 허용됩니다. 이 인터페이스는 §15.3.5에 설명된 대로 동일한 이름으로 상속된 멤버를 숨기게 지정합니다.
public, protected, internal및 private 한정자는 인터페이스의 접근성을 제어합니다. 인터페이스 선언이 발생하는 컨텍스트에 따라 이러한 한정자 중 일부만 허용될 수 있습니다(§7.5.2). 부분 형식 선언(§15.2.7)에 접근성 사양(public, protected, internal, 및 private 한정자)을 포함하는 경우, §15.2.2의 규칙이 적용됩니다.
19.2.3 Variant 형식 매개 변수 목록
19.2.3.1 일반
Variant 형식 매개 변수 목록은 인터페이스 및 대리자 형식에서만 발생할 수 있습니다. 일반 type_parameter_list와의 차이점은 각 형식 매개 변수에 대한 variance_annotation이 선택적이라는 점입니다.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: 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)으로 선언된 경우 각 부분 선언은 각 형식 매개 변수에 대해 동일한 분산을 지정해야 합니다.
19.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ᵢ는 출력에 안전하지 않음.
-
직관적으로 출력 위치에서는 출력 안전하지 않은 형식이 금지되고 입력 위치에서는 입력 안전하지 않은 형식이 금지됩니다.
형식은 출력 안전하지 않은 경우 출력에 안전 하며 입력 안전하지 않은 경우 입력이 안전 합니다.
19.2.3.3 분산 변환
분산 주석의 목적은 인터페이스 및 대리자 형식에 대한 보다 관대하지만 형식이 안전한 변환을 제공하는 것입니다. 이를 위해 암시적(§10.2) 및 명시적 변환(§10.3)의 정의는 다음과 같이 정의된 분산 변환성 개념을 사용합니다.
형식은 변형 형식 T<Aᵢ, ..., Aᵥ>T<Bᵢ, ..., Bᵥ> 매개 변수로 선언된 인터페이스 또는 대리자 형식인 경우 T 형식으로 분산 변환할 수 있으며 각 변형 형식 매개 변수 T<Xᵢ, ..., Xᵥ>Xᵢ 에 대해 다음 중 하나가 유지됩니다.
-
Xᵢ는 공변이며Aᵢ에서Bᵢ로의 암시적 참조 또는 ID 변환이 존재합니다. -
Xᵢ가 반공변이고Bᵢ에서Aᵢ로의 암시적 참조 또는 ID 변환이 존재합니다. -
Xᵢ는 불변이며Aᵢ에서Bᵢ로의 ID 변환이 존재합니다.
19.2.4 기본 인터페이스
인터페이스는 인터페이스의 명시적 기본인터페이스라고 하는 0개 이상의 인터페이스 형식에서 상속할 수 있습니다. 인터페이스에 하나 이상의 명시적 기본 인터페이스가 있는 경우 해당 인터페이스의 선언에서 인터페이스 식별자 뒤에 콜론과 기본 인터페이스 형식의 쉼표로 구분된 목록이 표시됩니다.
파생 인터페이스는 기본 인터페이스에서 선언된 상속된 멤버(§7.7.2.3)를 숨기거나 기본 인터페이스에 선언된 상속된 멤버(§19.6.2)를 명시적으로 구현하는 새 멤버를 선언할 수 있습니다.
interface_base
: ':' interface_type_list
;
명시적 기본 인터페이스는 인터페이스 형식(§8.4, §19.2)을 생성할 수 있습니다. 기본 인터페이스는 범위에 있는 형식 매개 변수를 포함할 수 있지만 자체적으로 형식 매개 변수가 될 수 없습니다.
생성된 인터페이스 형식의 경우 명시적 기본 인터페이스는 제네릭 형식 선언에서 명시적 기본 인터페이스 선언을 사용하고 기본 인터페이스 선언의 각 type_parameter 대해 생성된 형식의 해당 type_argument 대체하여 형성됩니다.
인터페이스의 명시적 기본 인터페이스는 최소한 인터페이스 자체(§7.5.5)만큼 액세스할 수 있어야 합니다.
참고: 예를 들어,
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 {}의 기본 인터페이스는
IComboBoxIControl,ITextBox및IListBox. 즉, 위의 인터페이스는IComboBox멤버SetText및SetItemsPaint.을 상속합니다.끝 예제
생성된 제네릭 형식에서 상속된 멤버는 형식 대체 후에 상속됩니다. 즉, 멤버의 모든 구성 요소 형식에는 기본 클래스 선언의 형식 매개 변수가 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에서 자세히 설명합니다.
인터페이스의 모든 기본 인터페이스는 출력 안전(§19.2.3.2)이어야 합니다.
19.3 인터페이스 본문
인터페이스의 interface_body 인터페이스의 멤버를 정의합니다.
interface_body
: '{' interface_member_declaration* '}'
;
19.4 인터페이스 멤버
19.4.1 일반
인터페이스의 멤버는 기본 인터페이스에서 상속된 멤버 및 인터페이스 자체에 의해 선언된 멤버입니다.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
이 절은 인터페이스에 대한 제한 사항으로 클래스(§15.3)의 멤버에 대한 설명을 보강합니다. 인터페이스 멤버는 다음 추가 규칙과 함께 member_declaration사용하여 선언됩니다.
- finalizer_declaration 허용되지 않습니다.
- constructor_declaration인스턴스 생성자는 허용되지 않습니다.
- 모든 인터페이스 멤버는 암시적으로 공용 액세스 권한을 갖습니다. 그러나 정적 생성자(§15.12)를 제외하고 명시적 액세스 한정자(§7.5.2)가 허용됩니다.
-
abstract한정자는 본문이 없는 인터페이스 함수 멤버에 대해 암시되며, 해당 한정자는 명시적으로 제공될 수 있습니다. - 본문을 포함하는 선언이 있는 인터페이스 인스턴스 함수 멤버는 또는
virtual한정자를 사용하지 않는 한sealed암시적private멤버입니다. 한virtual정자는 명시적으로 제공될 수 있습니다. - 인터페이스의 A
private또는sealed함수 멤버에는 본문이 있어야 합니다. - 함수 멤버에는 한
private정자가sealed없습니다. - 파생 인터페이스는 기본 인터페이스에 선언된 추상 또는 가상 멤버를 재정의할 수 있습니다.
- 명시적으로 구현된 함수 멤버에는 한정자가
sealed없습니다.
constant_declaration(§15.4)와 같은 일부 선언에는 인터페이스에 제한이 없습니다.
인터페이스의 상속된 멤버는 특히 인터페이스의 선언 공간에 속하지 않습니다. 따라서 인터페이스는 상속된 멤버와 동일한 이름 또는 서명을 가진 멤버를 선언할 수 있습니다. 이 경우 파생된 인터페이스 멤버는 기본 인터페이스 멤버를 숨깁니다 . 상속된 멤버를 숨기는 것은 오류로 간주되지 않지만 경고(§7.7.2.3)가 발생합니다.
상속된 멤버를 new 숨기지 않는 선언에 한정자가 포함된 경우, 해당 경우에 대한 경고 메시지가 발생합니다.
참고: 클래스
object의 멤버는 엄밀히 말하면 모든 인터페이스의 멤버가 아닙니다(§19.4). 그러나 클래스object의 멤버는 모든 인터페이스 형식(§12.5)의 멤버 조회를 통해 사용할 수 있습니다. 끝 메모
여러 부분으로 선언된 인터페이스의 멤버 집합(§15.2.7)은 각 파트에서 선언된 멤버의 합합입니다. 인터페이스 선언의 모든 부분의 본문은 동일한 선언 공간(§7.3)을 공유하고 각 멤버의 범위(§7.7)는 모든 부분의 본문으로 확장됩니다.
예: 멤버
IA및 속성M에 대한 구현이 있는 인터페이스P를 고려합니다. 구현 형식C은 둘 중 하나M또는P에 대한 구현을 제공하지 않습니다. 컴파일 시간 형식이 암시적으로 변환할IA수 있는 인터페이스인 참조를IB통해 액세스해야 합니다. 이러한 멤버는 형식C변수에 대한 멤버 조회를 통해 찾을 수 없습니다.interface IA { public int P { get { return 10; } } public void M() { Console.WriteLine("IA.M"); } } interface IB : IA { public new int P { get { return 20; } } void IA.M() { Console.WriteLine("IB.M"); } } class C : IB { } class Test { public static void Main() { C c = new C(); ((IA)c).M(); // cast needed Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed } }인터페이스 내에서 멤버
IAIB는M이름으로 직접 액세스할 수 있습니다. 그러나 메서드Main내에서는 해당 이름이 표시되지 않으므로 작성하거나c.M()작성c.P할 수 없습니다. 이를 찾으려면 적절한 인터페이스 형식으로 캐스팅해야 합니다. inM선언IB은 명시적 인터페이스 구현 구문을 사용합니다. 이는 해당 메서드가 in의IA메서드를 재정의하도록 하는 데 필요합니다. 한정자는override함수 멤버에 적용되지 않을 수 있습니다. 끝 예제
19.4.2 인터페이스 필드
이 절은 인터페이스에 선언된 필드의 경우 §15.5 클래스의 필드에 대한 설명을 보강합니다.
인터페이스 필드는 다음 추가 규칙과 함께 field_declarations(§15.5.1)를 사용하여 선언됩니다.
- field_declaration 인스턴스 필드를 선언하는 것은 컴파일 시간 오류입니다.
예: 다음 프로그램에는 다양한 종류의 정적 멤버가 포함되어 있습니다.
public interface IX { public const int Constant = 100; protected static int field; static IX() { Console.WriteLine("static members initialized"); Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); field = 50; Console.WriteLine("static constructor has run"); } } public class Test: IX { public static void Main() { Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); } }생성된 출력은 다음과 같습니다.
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50끝 예제
정적 필드의 할당 및 초기화에 대한 자세한 내용은 §19.4.8 을 참조하세요.
19.4.3 인터페이스 메서드
이 절은 인터페이스에 선언된 메서드의 경우 §15.6 클래스의 메서드 설명을 보강합니다.
인터페이스 메서드는 method_declarations(§15.6)를 사용하여 선언됩니다. 인터페이스 메서드 선언의 특성, return_type, ref_return_type, 식별자 및 parameter_list 클래스의 메서드 선언과 동일한 의미를 갖습니다. 인터페이스 메서드에는 다음과 같은 추가 규칙이 있습니다.
method_modifier 포함
override해서는 안 된다.본문이 세미콜론(
;)인 메서드입니다abstractabstract. 한정자는 필요하지 않지만 허용됩니다.블록 본문 또는 식 본문이 method_body 인터페이스 메서드 선언입니다
virtualvirtual. 한정자는 필요하지 않지만 허용됩니다.method_declaration type_parameter_list 없는 한 type_parameter_constraints_clause 갖지 않습니다.
클래스 메서드에 대해 명시된 유효한 한정자 조합에 대한 요구 사항 목록은 다음과 같이 확장됩니다.
- extern이 아닌 정적 선언은 블록 본문 또는 식 본문을 method_body 합니다.
- extern이 아닌 가상 선언은 블록 본문 또는 식 본문을 method_body 합니다.
- extern이 아닌 프라이빗 선언은 블록 본문 또는 식 본문을 method_body 합니다.
- extern이 아닌 봉인된 선언은 블록 본문 또는 식 본문을 method_body 합니다.
- 비동기 선언은 블록 본문 또는 식 본문을 method_body 합니다.
인터페이스 메서드의 모든 매개 변수 형식은 입력 안전(§19.2.3.2)이어야 하며 반환 형식은
void출력이 안전해야 합니다.모든 출력 또는 참조 매개 변수 형식도 출력에 안전해야 합니다.
참고: 출력 매개 변수는 일반적인 구현 제한으로 인해 입력이 안전해야 합니다. 끝 메모
메서드의 모든 형식 매개 변수에 대한 각 클래스 형식 제약 조건, 인터페이스 형식 제약 조건 및 형식 매개 변수 제약 조건은 입력로부터 안전해야 합니다.
이러한 규칙은 인터페이스의 공변 또는 반공변 사용이 형식 안전 상태로 유지되도록 합니다.
예제:
interface I<out T> { void M<U>() where U : T; // Error }형식 매개 변수 제약으로
T에U를 사용하는 것이 입력에 안전하지 않기 때문에 형식이 잘못되었습니다.이 제한 사항이 없는 경우 다음과 같은 방식으로 형식 안전을 위반할 수 있습니다.
interface I<out T> { void M<U>() where U : T; } class B {} class D : B {} class E : B {} class C : I<D> { public void M<D>() {...} } ... I<B> b = new C(); b.M<E>();이것은 실제로
C.M<E>로의 호출입니다. 그러나 이 호출이E에서D로 파생되어야 하므로 타입 안전성이 여기에서 위반됩니다.끝 예제
참고: 구현이 있는 정적 메서드를 표시할 뿐만 아니라 해당 메서드가 호출 되고 올바른 반환 형식과 시그니처가 있으므로 진입점이기도 한 예제는
Main를 참조하세요. 끝 메모
인터페이스에 선언된 구현이 있는 가상 메서드는 파생 인터페이스에서 추상화되도록 재정의될 수 있습니다. 이를 재지정이라고 합니다.
예제:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }이는 메서드의 구현이 적절하지 않고 클래스를 구현하여 보다 적절한 구현을 제공해야 하는 파생 인터페이스에서 유용합니다. 끝 예제
19.4.4 인터페이스 속성
이 절은 인터페이스에 선언된 속성에 대한 §15.7 클래스의 속성 설명을 보강합니다.
인터페이스 속성은 다음 추가 규칙과 함께 property_declarations(§15.7.1)를 사용하여 선언됩니다.
property_modifier 포함
override해서는 안 된다.명시적 인터페이스 멤버 구현에는 accessor_modifier (§15.7.3)가 포함되지 않습니다.
파생 인터페이스는 기본 인터페이스에 선언된 추상 인터페이스 속성을 명시적으로 구현할 수 있습니다.
참고: 인터페이스에 인스턴스 필드가 포함될 수 없으므로 인터페이스 속성은 암시적 숨겨진 인스턴스 필드의 선언이 필요하므로 인스턴스 자동 속성이 될 수 없습니다. 끝 메모
인터페이스 속성의 형식은 get 접근자가 있는 경우 출력이 안전하며 set 접근자가 있는 경우 입력이 안전해야 합니다.
블록 본문 또는 식 본문이 method_body 인터페이스 메서드 선언입니다
virtualvirtual. 한정자는 필요하지 않지만 허용됩니다.구현
abstract이 없는abstract한정자는 필요하지 않지만 허용됩니다. 자동으로 구현된 속성(§15.7.4)으로 간주되지 않습니다.
19.4.5 인터페이스 이벤트
이 절은 인터페이스에 선언된 이벤트에 대해 §15.8 클래스의 이벤트에 대한 설명을 보강합니다.
인터페이스 이벤트는 다음과 같은 추가 규칙과 함께 event_declarations(§15.8.1)를 사용하여 선언됩니다.
-
event_modifier 포함하지
override않습니다. - 파생 인터페이스는 기본 인터페이스(§15.8.5)에 선언된 추상 인터페이스 이벤트를 구현할 수 있습니다.
- 인스턴스 event_declaration variable_declaratorsvariable_initializer 포함하는 컴파일 시간 오류입니다.
- 또는
virtual한정자가 있는sealed인스턴스 이벤트는 접근자를 선언해야 합니다. 자동으로 구현된 필드와 유사한 이벤트(§15.8.2)로 간주되지 않습니다. - 한정자가 있는
abstract인스턴스 이벤트는 접근자를 선언해서는 안 됩니다. - 인터페이스 이벤트의 형식은 입력이 안전해야 합니다.
19.4.6 인터페이스 인덱서
이 절은 인터페이스에 선언된 인덱서 의 경우 §15.9 클래스의 인덱서 설명을 보강합니다.
인터페이스 인덱서는 다음 추가 규칙과 함께 indexer_declarations(§15.9)를 사용하여 선언됩니다.
indexer_modifier 포함
override해서는 안 된다.식 본문이 있거나 블록 본문 또는 식
virtual본문이 있는 접근자를 포함하는virtual. 한정자는 필요하지 않지만 허용됩니다.접근자 본문이 세미콜론()
;abstract인abstract; 한정자는 필요하지 않지만 허용됩니다.인터페이스 인덱서의 모든 매개 변수 형식은 입력이 안전해야 합니다(§19.2.3.2).
모든 출력 또는 참조 매개 변수 형식도 출력에 안전해야 합니다.
참고: 출력 매개 변수는 일반적인 구현 제한으로 인해 입력이 안전해야 합니다. 끝 메모
인터페이스 인덱서의 형식은 get 접근자가 있는 경우 출력에 안전하며 set 접근자가 있는 경우 입력이 안전해야 합니다.
19.4.7 인터페이스 연산자
이 절은 인터페이스에 선언된 연산자의 경우 §15.10 클래스의 operator_declaration 멤버에 대한 설명을 보강합니다.
인터페이스의 operator_declaration 구현입니다(§19.1).
인터페이스가 변환, 같음 또는 같지 않음 연산자를 선언하는 것은 컴파일 시간 오류입니다.
19.4.8 인터페이스 정적 생성자
이 절은 인터페이스에 선언된 정적 생성자에 대한 §15.12 클래스의 정적 생성자에 대한 설명을 보강합니다.
닫힌(§8.4.3) 인터페이스에 대한 정적 생성자는 지정된 애플리케이션 도메인에서 한 번만 실행됩니다. 정적 생성자의 실행은 애플리케이션 도메인 내에서 발생할 다음 작업 중 첫 번째 작업에 의해 트리거됩니다.
- 인터페이스의 정적 멤버가 참조됩니다.
- 실행이
Main시작되는 메서드(Main)를 포함하는 인터페이스에 대해 메서드를 호출하기 전에 - 해당 인터페이스는 멤버에 대한 구현을 제공하며 해당 구현은 해당 멤버에 대한 가장 구체적인 구현(§19.4.10)으로 액세스됩니다.
참고: 앞의 작업이 수행되지 않는 경우 인터페이스를 구현하는 형식의 인스턴스가 만들어지고 사용되는 프로그램에 대해 인터페이스의 정적 생성자가 실행되지 않을 수 있습니다. 끝 메모
새 닫힌 인터페이스 형식을 초기화하려면 먼저 특정 닫힌 형식에 대한 새 정적 필드 집합을 만듭니다. 각 정적 필드는 기본값으로 초기화됩니다. 다음으로 정적 필드 이니셜라이저는 해당 정적 필드에 대해 실행됩니다. 마지막으로 정적 생성자가 실행됩니다.
참고: 인터페이스 내에서 선언된 다양한 종류의 정적 멤버(Main 메서드 포함)를 사용하는 예제는 §19.4.2 를 참조하세요. 끝 메모
19.4.9 인터페이스 중첩 형식
이 절은 인터페이스에 선언된 중첩 형식의 경우 §15.3.9 클래스의 중첩 형식에 대한 설명을 보강합니다.
variance_annotation(§19.2.3.1)로 선언된 형식 매개 변수의 범위 내에서 클래스 형식, 구조체 형식 또는 열거형 형식을 선언하는 것은 오류입니다.
예: 아래 선언
C은 오류입니다.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }끝 예제
19.4.10 가장 구체적인 구현
모든 클래스와 구조체는 형식 또는 직접 및 간접 인터페이스에 표시되는 구현 중 해당 형식으로 구현된 모든 인터페이스에서 선언된 모든 가상 멤버에 대해 가장 구체적인 구현을 가져야 합니다. 가장 구체적인 구현은 다른 모든 구현보다 더 구체적인 고유한 구현입니다.
참고: 가장 구체적인 구현 규칙은 다이아몬드 인터페이스 상속에서 발생하는 모호성이 충돌이 발생하는 지점에서 프로그래머에 의해 명시적으로 해결되도록 합니다. 끝 메모
인터페이스를 구현하고 멤버를 선언하는 인터페이스 TI2I3I2에서 I3 직접 또는 간접적으로 파생되는 구조체 또는 클래스인 형식 I 의 M 경우 가장 구체적인 구현은 다음과 같습니다.M
- 구현
T을 선언하는 경우I.M해당 구현이 가장 구체적인 구현입니다. - 그렇지 않으면 클래스이고 직접 또는 간접 기본 클래스가 구현
T을 선언하는 경우I.M가장 파생된 기본 클래스T는 가장 구체적인 구현입니다. - 그렇지 않으면
I2I3직접 또는 간접적으로T구현되고I3파생I2되는I3.M인터페이스가 보다I2.M구체적인 구현입니다. - 그렇지 않으면
I2.M더 구체적이지도 않고I3.M오류가 발생합니다.
예제:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB : IA { void IA.M() { Console.WriteLine("IB.M"); } } interface IC: IA { void IA.M() { Console.WriteLine("IC.M"); } } abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M' abstract class D: IA, IB, IC // OK { public abstract void M(); }가장 구체적인 구현 규칙은 충돌이 발생하는 시점에 프로그래머가 충돌(즉, 다이아몬드 상속에서 발생하는 모호성)을 명시적으로 해결하도록 합니다. 끝 예제
19.4.11 인터페이스 멤버 액세스
인터페이스 멤버는 폼 의 멤버 액세스(§12.8.7) 및 인덱서 액세스(I.M) 식을 통해 액세스하며I[A], 여기서 I 인터페이스 형식은 해당 인터페이스 형식 M 의 상수, 필드, 메서드, 속성 또는 이벤트이며 A 인덱서 인수 목록입니다.
직접 또는 간접적으로 인터페이스 D 를 구현하고 B 메서드B를 정의하는 직접 또는 간접 기본 클래스가 있는 I 클래스IM()에서 식 base.M() 은 정적으로(base.M())가 클래스 형식의 구현에 바인딩되는 경우에만 M() 유효합니다.
엄격하게 단일 상속인 인터페이스의 경우(상속 체인의 각 인터페이스에 정확히 0개 또는 하나의 직접 기본 인터페이스가 있음), 멤버 조회(§12.5), 메서드 호출(§12.8.1)의 효과 0.2) 및 인덱서 액세스(§12.8.12.4) 규칙은 클래스 및 구조체의 경우와 정확히 동일합니다. 파생 멤버가 많을수록 이름이나 서명이 같은 파생 멤버를 더 적게 숨깁니다. 그러나 다중 상속 인터페이스의 경우 관련이 없는 두 개 이상의 기본 인터페이스가 동일한 이름 또는 서명으로 멤버를 선언할 때 모호성이 발생할 수 있습니다. 이 하위 클래스는 몇 가지 예를 보여 줍니다. 그 중 일부는 모호성으로 이어지며 다른 예제는 그렇지 않습니다. 모든 경우에 명시적 캐스트를 사용하여 모호성을 해결할 수 있습니다.
예제: 다음 코드에서
interface IList { int Count { get; set; } } interface ICounter { int Count { get; set; } } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count } }첫 번째 문장은 내의
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.4)을 적용하여IInteger.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로의 경로에서도 멤버가 숨겨집니다.끝 예제
19.5 정규화된 인터페이스 멤버 이름
인터페이스 멤버는 때때로 한정된 인터페이스 멤버 이름으로 참조됩니다. 인터페이스 멤버의 정규화된 이름은 멤버가 선언된 인터페이스의 이름과 점, 멤버의 이름으로 구성됩니다. 멤버의 정규화된 이름은 멤버가 선언된 인터페이스를 참조합니다.
예제: 주어진 선언들
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }선행 명칭
Paint은IControl.Paint이고, SetText의 선행 명칭은ITextBox.SetText입니다. 위의 예제에서는Paint를ITextBox.Paint로 참조할 수 없습니다.끝 예제
인터페이스가 네임스페이스의 일부인 경우 정규화된 인터페이스 멤버 이름에는 네임스페이스 이름이 포함될 수 있습니다.
예제:
namespace GraphicsLib { interface IPolygon { void CalculateArea(); } }네임스페이스
GraphicsLib내에서,IPolygon.CalculateArea및GraphicsLib.IPolygon.CalculateArea는CalculateArea메서드의 정규화된 인터페이스 멤버 이름입니다.끝 예제
19.6 인터페이스 구현
19.6.1 일반
인터페이스는 클래스 및 구조체에 의해 구현될 수 있습니다. 클래스 또는 구조체가 인터페이스를 직접 구현함을 나타내기 위해 인터페이스는 클래스 또는 구조체의 기본 클래스 목록에 포함됩니다.
인터페이스 C 를 구현하는 클래스 또는 구조체 I 는 액세스할 수 있는 선언된 IC 모든 멤버에 대한 구현을 제공하거나 상속해야 합니다. 공용 멤버는 .의 I 공용 멤버 C에 정의될 수 있습니다. 액세스할 수 있는 I 공용이 아닌 멤버는 명시적 인터페이스 구현(C)을 사용하여 정의 C 할 수 있습니다.
인터페이스 매핑(§19.6.5)을 충족하지만 일치하는 기본 인터페이스 멤버를 구현하지 않는 파생 형식의 멤버는 새 멤버를 도입합니다. 이는 인터페이스 멤버를 정의하기 위해 명시적 인터페이스 구현이 필요한 경우에 발생합니다.
예제:
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, §19.2)을 생성할 수 있습니다.
예제: 다음 코드는 클래스가 생성된 인터페이스 형식을 구현하는 방법을 보여 줍니다.
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}끝 예제
제네릭 클래스 선언의 기본 인터페이스는 §19.6.3에 설명된 고유성 규칙을 충족해야 합니다.
19.6.2 명시적 인터페이스 멤버 구현
인터페이스를 구현하기 위해 클래스, 구조체 또는 인터페이스는 명시적 인터페이스 멤버 구현을 선언할 수 있습니다. 명시적 인터페이스 멤버 구현은 정규화된 인터페이스 멤버 이름을 참조하는 메서드, 속성, 이벤트 또는 인덱서 선언입니다. 기본 인터페이스에서 public이 아닌 멤버를 구현하는 클래스 또는 구조체는 명시적 인터페이스 멤버 구현을 선언해야 합니다. 기본 인터페이스에서 멤버를 구현하는 인터페이스는 명시적 인터페이스 멤버 구현을 선언해야 합니다.
인터페이스 매핑을 충족하는 파생 인터페이스 멤버(§19.6.5)는 기본 인터페이스 멤버(§7.7.2)를 숨깁니다. 한정자가 없는 한 new 컴파일러는 경고를 실행해야 합니다.
예제:
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메서드를 구현할 수 있습니다.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); } }끝 예제
메서드 호출, 속성 액세스, 이벤트 액세스 또는 인덱서 액세스에서 정규화된 인터페이스 멤버 이름을 통해 명시적 인터페이스 멤버 구현에 액세스할 수 없습니다. 명시적 인터페이스 인스턴스 멤버 구현은 인터페이스 인스턴스를 통해서만 액세스할 수 있으며, 이 경우 멤버 이름으로만 참조됩니다. 명시적 인터페이스 정적 멤버 구현은 인터페이스 이름을 통해서만 액세스할 수 있습니다.
명시적 인터페이스 멤버 구현에 , extern 외의 다른 한정자(async)를 포함하는 것은 컴파일 시간 오류입니다.
명시적 인터페이스 메서드 구현은 인터페이스에서 모든 형식 매개 변수 제약 조건을 상속합니다.
명시적 인터페이스 메서드 구현에 대한 type_parameter_constraints_clause 각각 class형식으로 상속된 제약 조건에 따라 알려진 struct적용된 primary_constraint 또는 primary_constraint만 구성 될 수 있습니다. 형식 매개 변수인 명시적 인터페이스 메서드 구현의 서명에 있는 T? 양식 T 의 모든 형식은 다음과 같이 해석됩니다.
- 형식 매개 변수
class에T에 대한 제약 조건이 추가되면T?는 nullable 참조 형식이지만, 그렇지 않으면 - 추가된 제약 조건이 없거나 제약 조건
struct가 추가되면, 형식 매개 변수T에 대해T?가 nullable 값 형식이 됩니다.
예제: 다음은 형식 매개 변수가 관련될 때 규칙이 작동하는 방식을 보여 줍니다.
#nullable enable interface I { void Foo<T>(T? value) where T : class; void Foo<T>(T? value) where T : struct; } class C : I { void I.Foo<T>(T? value) where T : class { } void I.Foo<T>(T? value) where T : struct { } }형식 매개 변수 제약 조건이
where T : class없으면 참조 형식 형식 형식 매개 변수를 사용하는 기본 메서드를 재정의할 수 없습니다. 끝 예제
참고: 명시적 인터페이스 멤버 구현에는 다른 멤버와 다른 접근성 특성이 있습니다. 명시적 인터페이스 멤버 구현은 메서드 호출 또는 속성 액세스에서 정규화된 인터페이스 멤버 이름을 통해 액세스할 수 없으므로 전용입니다. 그러나 인터페이스를 통해 액세스할 수 있으므로 선언된 인터페이스처럼 공용이라는 의미도 있습니다. 명시적 인터페이스 멤버 구현은 다음 두 가지 주요 용도로 사용됩니다.
- 명시적 인터페이스 멤버 구현은 클래스 또는 구조체 인스턴스를 통해 액세스할 수 없으므로 클래스 또는 구조체의 공용 인터페이스에서 인터페이스 구현을 제외할 수 있습니다. 이는 클래스 또는 구조체가 해당 클래스 또는 구조체의 소비자에게 관심이 없는 내부 인터페이스를 구현할 때 특히 유용합니다.
- 명시적 인터페이스 멤버 구현을 사용하면 동일한 서명을 가진 인터페이스 멤버를 명확하게 구분할 수 있습니다. 명시적 인터페이스 멤버 구현이 없으면 클래스, 구조체 또는 인터페이스가 동일한 서명 및 반환 형식을 가진 인터페이스 멤버의 다른 구현을 갖는 것은 불가능합니다. 클래스, 구조체 또는 인터페이스가 시그니처가 동일하지만 반환 형식이 다른 모든 인터페이스 멤버에서 구현하는 것은 불가능합니다.
끝 메모
명시적 인터페이스 멤버 구현이 유효하려면 클래스, 구조체 또는 인터페이스가 정규화된 인터페이스 멤버 이름, 형식, 형식 매개 변수 수 및 매개 변수 형식이 명시적 인터페이스 멤버 구현과 정확히 일치하는 멤버를 포함하는 기본 클래스 또는 기본 인터페이스 목록의 인터페이스 이름을 지정해야 합니다. 인터페이스 함수 멤버에 매개 변수 배열이 있는 경우 연결된 명시적 인터페이스 멤버 구현의 해당 매개 변수는 한정자를 가질 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로 작성해야 합니다.끝 예제
19.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 (Derived<int,int>')에서 I<int>메서드를 호출합니다 .
19.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: objectobject지정할 필요(또는 허용되지 않음)는 아닙니다. 인터페이스 형식 매개 변수가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, §19.6.2). 끝 메모
19.6.5 인터페이스 매핑
클래스 또는 구조체는 클래스 또는 구조체의 기본 클래스 목록에 나열된 인터페이스의 모든 추상 멤버 구현을 제공해야 합니다. 구현 클래스 또는 구조체에서 인터페이스 멤버의 구현을 찾는 프로세스를 인터페이스 매핑이라고 합니다.
클래스 또는 구조체 C 에 대한 인터페이스 매핑은 기본 클래스 목록에 C지정된 각 인터페이스의 각 멤버에 대한 구현을 찾습니다. 멤버 I.M 가 선언되는 인터페이스인 특정 인터페이스 멤버 IM의 구현은 각 클래스, 인터페이스 또는 구조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이벤트이며 이름과 형식AB은 동일합니다. -
AB은 인덱서이고 형식 및 매개 변수 목록이AB동일하며A동일한 접근자를B갖습니다(A명시적 인터페이스 멤버 구현이 아닌 경우 추가 접근자를 포함하도록 허용됨).
인터페이스 매핑 알고리즘의 주목할 만한 의미는 다음과 같습니다.
- 명시적 인터페이스 멤버 구현은 인터페이스 멤버를 구현하는 클래스 또는 구조체 멤버를 결정할 때 동일한 클래스 또는 구조체의 다른 멤버보다 우선합니다.
- 비공용 멤버나 정적 멤버 모두 인터페이스 매핑에 참여하지 않습니다.
예제: 다음 코드에서
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }명시적 인터페이스 멤버 구현이 다른 멤버보다 우선하기 때문에,
ICloneable.Clone의C멤버는Clone의ICloneable의 구현이 됩니다.끝 예제
클래스 또는 구조체가 이름, 형식 및 매개 변수 형식이 같은 멤버를 포함하는 둘 이상의 인터페이스를 구현하는 경우 각 인터페이스 멤버를 단일 클래스 또는 구조체 멤버에 매핑할 수 있습니다.
예제:
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의 동일한 구현을 공유하고,ComboBox는 단순히 세 개의 인터페이스인IControl,ITextBox, 및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입니다.끝 예제
19.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를 호출하면 다음과 같은 효과가 발생합니다.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의 구현을 특수화할 수 있습니다.끝 예제
19.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에 매핑합니다.끝 예제
19.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