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; } }

이제 MyButtonMyTextBlock 은 상호 교체가능 (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/

반응형

+ Recent posts