Section 41.3: 인터페이스 관련 기본 사항들
인터페이스는 특정 기능의 "계약서 (contract)" 와도 같다고 알려져 있다. 이는 인터페이스가 메소드나 속성 등을 선언하고는 있지만 실제 구현을 하지는 않는다는 것을 의미한다.
따라서, 인터페이스는 클래스와 다르게:
- 인스턴스화 (instantiate) 될 수 없다
- 기능 자체를 제공하지는 않는다
- 메소드만을 가질 수 있다 * (속성과 이벤트는 내부적으로 모두 메소드이다)
- 인터페이스를 상속받는 작업은 "구현 (implement)" 한다고 한다
- 클래스의 경우 하나의 클래스로부터만 상속을 받을 수 있지만, 인터페이스의 경우에는 복수개의 인터페이스를 "구현" 할 수 있다
public interface ICanDoThis{
void TheThingICanDo();
int SomeValueProperty { get; set; }
}
몇가지 주목할 점:
- "I" 접두어는 인터페이스에 대한 관용적 명명법 (naming convention) 이다.
- 메소드의 본문 대신 세미콜론 ";" 만 기술한다.
- 속성 (property) 도 내부적으로는 메소드이기에 함께 사용 가능하다.
public class MyClass: ICanDoThis {
public void TheThingICanDo() {
// 작업을 수행한다
}
public int SomeValueProperty {
get;
set;
}
public int SomeValueNotImplentingAnything {
get;
set;
}
}
ICanDoThis obj = new MyClass();
// 문제 없음
obj.TheThingICanDo();
// 문제 없음
obj.SomeValueProperty = 5;
// 에러, 인터페이스에는 정의되어 있지 않은 멤버이다
obj.SomeValueNotImplentingAnything = 5;
// 클래스에만 존재하는 속성에 접근하고자 한다면 이를 "down cast" 하여야 한다
((MyClass)obj).SomeValueNotImplemtingAnything = 5; // 문제 없음
이는 WinForms 나 WPF 등의 UI 프레임웍 기반의 코드를 작성할 때 매우 유용하다. 이러한 프레임웍들은 사용자 컨트롤들 (user control) 을 만들 때 특정 기반 클래스를 의무적으로 상속받게 되어 있어, 각기 다른 사용자 컨트롤들에 대한 추상화를 제공할 기회를 놓칠 수 있기 때문이다. 다음의 예제를 확인한다:
public class MyTextBlock: TextBlock {
public void SetText(string str) {
this.Text = str;
}
}
public class MyButton: Button {
public void SetText(string str) {
this.Content = str;
}
}
문제가 되는 점은, 두 사용자 컨트롤들 모두 "텍스트" 관련 개념을 가지고는 있지만, 실제 속성 값 이름이 다르다는 점이다. 또한 이를 위해 추상 기반 클래스 (abstract base class) 를 만들 수도 없는데, 이는 각자가 의무적으로 두개의 다른 클래스를 이미 상속하고 있기 때문이다. 이런 문제를 인터페이스를 통해 해결할 수 있다.
public interface ITextControl {
void SetText(string str);
}
public class MyTextBlock: TextBlock, ITextControl {
public void SetText(string str) {
this.Text = str;
}
}
public class MyButton: Button, ITextControl {
public void SetText(string str) {
this.Content = str;
}
public int Clicks {
get;
set;
}
}
이제 MyButton
과 MyTextBlock
은 상호 교체가능 (interchangeable) 하다.
var controls = new List < ITextControls > {
new MyTextBlock(),
new MyButton()
};
foreach(var ctrl in controls) {
ctrl.SetText("This text will be applied to both controls despite them being different");
// 인터페이스에 해당 멤버가 존재하지 않으므로 아래 코드는 컴파일 에러를 발생시킨다.
ctrl.Clicks = 0;
// 첫번째 클래스가 실제 버튼 클래스가 아니므로 올바르지 않은 타입 변환으로 인해 런타임 에러를 발생시킨다.
((MyButton) ctrl).Clicks = 0;
/* 해결책으로, 클래스의 타입을 먼저 확인해 볼 수 있다.
그러나 이러한 접근 방식은 잘못된 추상화의 조짐이므로
종종 옳지 않은 관행으로 여겨진다. */
var button = ctrl as MyButton;
if (button != null)
button.Clicks = 0; // 에러 발생하지 않음
}
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.
[출처] https://books.goalkicker.com/CSharpBook/
반응형
'번역 > C# Notes for Professionals' 카테고리의 다른 글
41.5: 다중 인터페이스 구현 (implement) 하기 (0) | 2022.04.23 |
---|---|
41.4: 인터페이스 구현 예제로써의 IComparable<T> (0) | 2022.04.21 |
41.2: 명시적인 인터페이스 구현 (implementation) 방법 (0) | 2022.04.11 |
41.1: 인터페이스 구현 (implement) 하기 (0) | 2022.04.08 |
40.6: protected 액세스 한정자 (0) | 2022.04.07 |