개발꿈나무
[C# 교과서] 24. C# 활용(18) - 인터페이스 본문
인터페이스
인터페이스는 프로그램의 표준 규약을 정하고 따를 수 있도록 강제한다.
인터페이스
인터페이스는 클래스 또는 구조체에 포함될 수 있는 관련 있는 메서드들을 묶어 관리하며, 인터페이스를 상속받아 그 내용을 구현하는 클래스는 인터페이스에 선언된 멤버(속성, 메서드 등)가 반드시 구현되어 있다는 보증을 한다.
- 인터페이스는 interface 키워드를 사용하여 만들며 실행 가능한 코드와 데이터를 포함하지 않는다.
- 추상 클래스처럼 다른 클래스의 멤버 이름을 미리 정의할 때 사용된다. 추상 클래스와 다른 점은 멤버 내용을 구현하지 않고 이름만 정의한다.
- 인터페이스에는 메서드, 속성, 인덱서 및 이벤트를 정의할 수 있다.
- 현실 세계에서 전 세계 표준과 같은 기능이다.
- 단일 상속만 지원하는 클래스와 달리 인터페이스를 사용한 다중 상속이 가능하다.
- 인터페이스 멤버는 액세스 한정자를 붙이지 않으며 항상 public이고, virtual 및 static을 붙일 수 없다.
- 인터페이스 내의 모든 멤버는 기본적으로 public이다.
- C#에서 인터페이스 이름은 대문자 I로 시작한다.
- 인터페이스는 인스턴스화되지 않고 클래스를 사용하여 인스턴스화 된다.
- 인터페이스는 계약(constract) 의미가 강하며 속성, 메서드, 이벤트, 인덱서 등 구조를 미리 정의한다.
인터페이스로 특정 멤버가 반드시 구현되어야 함을 보증하기
using System;
namespace InterfaceNote
{
//[1] ICar 인터페이스 선언
interface ICar
{
void Go(); //[A] 메서드 시그니처만 제공
}
//[2] ICar 인터페이스를 상속하는 Car 클래스 선언
class Car : ICar
{
public void Go() => Console.WriteLine(
"상속한 인터페이스에 정의된 모든 멤버를 반드시 구현해야한다.");
}
class InterfaceNote
{
static void Main()
{
var car = new Car();
car.Go();
}
}
}
ICar 인터페이스를 상속받는 클래스에서 Go() 메서드를 구현하지 않는 경우 에러가 발생한다.
인터페이스 형식 개체에 인스턴스 담기
using System;
// [1] 하나의 멤버를 갖는 인터페이스 정의
public interface IRepository
{
void Get();
}
// [2] 인터페이스를 상속하는 클래스 구현
public class Repository : IRepository
{
public void Get()
{
Console.WriteLine("Get() 메서드를 구현해야 합니다.");
}
}
class InterfacePractice
{
static void Main()
{
// [A] 인터페이스 형식 개체에 인스턴스 담기
IRepository repository = new Repository();
repository.Get();
}
}
인터페이스를 상속하는 클래스를 이용하여 인터페이스를 인스턴스화할 수 있다.
이러한 코드 모양은 리포지토리(repository) 패턴 이름으로 많이 사용된다.
생성자의 매개변수에 인터페이스 사용하기
생성자의 매개변수에 인터페이스 형식을 사용하면 해당 인터페이스를 상속하는 모든 클래스의 인스턴스를 받을 수 있다.
using System;
namespace InterfaceDemo
{
// 배터리 표준(강제성)
interface IBattery
{
string GetName(); // 메서드 시그니처만 표시
}
class Good : IBattery
{
public string GetName() => "Good";
}
class Bad : IBattery
{
public string GetName() => "Bad";
}
class Car
{
private IBattery _battery;
//[1] 생성자의 매개 변수로 인터페이스 형식 지정
public Car(IBattery battery)
{
_battery = battery; // 넘어온 개체가 _battery 필드에 저장
}
public void Run() => Console.WriteLine(
"{0} 배터리를 장착한 자동차가 달립니다.", _battery.GetName());
}
class InterfaceDemo
{
static void Main(string[] args)
{
//[A] 넘겨주는 개체에 따라서 배터리 이름이 다르게 표시
var good = new Car(new Good()); good.Run();
new Car(new Bad()).Run(); // 개체 만들기와 동시에 메서드 실행
}
}
}
IBattery 인터페이스를 상속하는 Good과 Bad 클래스의 인스턴스는 IBattery 인터페이스 형식을
매개변수로 받을 수 있다. 이러한 방식으로 생성자의 매개변수로 인터페이스를 사용하면, 해당 클래스의 생성자는
개체를 하나 이상 받을 수 있는 융통성이 늘어난다.
인터페이스를 사용한 다중 상속 구현하기
다중 상속은 클래스 하나를 콤마로 구분해서 인터페이스 하나 이상을 상속하는 것이다.
C#에서 클래스는 클래스에 대한 단일 상속만 지원하는 대신, 인터페이스는 클래스에 인터페이스를 하나 이상 상속할 수 있다.
using System;
namespace InterfaceInheritance
{
interface IAnimal
{
void Eat();
}
interface IDog
{
void Yelp();
}
class Dog : IAnimal, IDog // 인터페이스를 사용한 다중 상속
{
public void Eat() => Console.WriteLine("먹다.");
public void Yelp() => Console.WriteLine("짖다.");
}
class InterfaceInheritance
{
static void Main()
{
Dog dog = new Dog();
dog.Eat(); //[A] IAnimal 인터페이스 상속
dog.Yelp(); //[B] IDog 인터페이스 상속
}
}
}
Dog 클래스는 IAnimal 인터페이스와 IDog 인터페이스에서 다중 상속을 받는다.
명시적인 인터페이스 구현하기
인터페이스를 사용한 다중 상속이 이루어졌을 때 각 인터페이스에 동일한 멤버가 구현되어 있을 수 있다. 이 때는
명시적으로 어떤 인터페이스의 멤버를 실행할 지 지정해줘야 한다.
using System;
interface IDog
{
void Eat();
}
interface ICat
{
void Eat();
}
class Pet : IDog, ICat
{
void IDog.Eat() => Console.WriteLine("Dog Eat"); // [1] 명시적으로 IDog 지정
void ICat.Eat() => Console.WriteLine("Cat Eat"); // [2] 명시적으로 ICat 지정
}
class InterfaceExplicit
{
static void Main()
{
Pet pet = new Pet();
((IDog)pet).Eat(); // [A] pet 개체를 IDog 형식으로 형식 변환
((ICat)pet).Eat(); // [B] pet 개체를 ICat 형식으로 형식 변환
IDog dog = new Pet();
dog.Eat();
ICat cat = new Pet();
cat.Eat();
}
}
IDog 인터페이스와 ICat 인터페이스를 상속받는 Pet 클래스의 인스턴스 pet에서 IDog 인터페이스의
Eat() 메서드를 실행하려면 IDog 형식으로 변환해야 한다.
인터페이스와 추상 클래스 비교하기
<추상 클래스>
- 구현된 코드가 들어온다. 즉, 메서드 시그니처만 있는 것이 아니라 사용 가능한 실제로 구현된 메서드도 들어온다.
- 단일상속: 기본 클래스에서 상속될 수 있다.
- 각 멤버는 액세스 한정자를 갖는다.
- 필드, 속성, 생성자, 소멸자, 메서드, 이벤트, 인덱서 등을 갖는다.
<인터페이스>
- 인터페이스는 규약이다.
- 구현된 코드가 없다.
- 다중상속: 여러가지 인터페이스에서 상속 가능하다.
- 모든 멤버는 자동으로 public이다.
- 속성, 메서도, 이벤트와 대리자를 멤버로 갖는다.
IEnumerator 인터페이스 사용하기
using System;
using System.Collections;
class IEnumeratorDemo
{
static void Main()
{
string[] names = { "닷넷코리아", "비주얼아카데미" };
//[1] foreach 문으로 출력
foreach (string name in names)
{
Console.WriteLine(name);
}
//[2] IEnumerator 인터페이스를 통한 데이터 출력: foreach문과 동일
IEnumerator list = names.GetEnumerator(); // 하나씩 열거
while (list.MoveNext()) // 값이 있는 동안 반복
{
Console.WriteLine(list.Current); // 현재 반복중인 데이터 출력
}
}
}
IEnumerator 인터페이스는 문자열 배열 등 GetEnumerator() 메서드의 결괏값을 담아
MoveNext() 메서드로 값이 있는지 확인하고, Current 속성으로 현재 반복되는 데이터를 가져다 사용할 수 있다.
IDisposable 인터페이스 사용하기
using System;
class IDisposableDemo
{
static void Main()
{
Console.WriteLine("[1] 열기");
using (var t = new Toilet())
{
// 특정 프로세스 종료시 자동으로 닫기 수행
Console.WriteLine("[2] 사용");
}
}
}
public class Toilet : IDisposable
{
public void Dispose()
{
Console.WriteLine("[3] 닫기");
}
}
IDisposable 인터페이스를 상속하는 클래스의 Dispose() 메서드는 해당 클래스의 개체를 다 사용한 후
마지막으로 호출해서 정리하는 역할을 한다.
<Reference>
'C# 기초' 카테고리의 다른 글
[C# 교과서] 26. C# 확장 기능(1) - 확장 메서드 만들기 (0) | 2022.01.25 |
---|---|
[C# 교과서] 25. C# 활용(19) - 특성과 리플렉션 (0) | 2022.01.25 |
[C# 교과서] 23. C# 활용(17) - 메서드 오버라이드 (0) | 2022.01.20 |
[C# 교과서] 22. C# 활용(16) - 상속으로 클래스 확장하기 (0) | 2022.01.20 |
[C# 교과서] 21. C# 활용(15) - 대리자(delegate), 이벤트(Event) (0) | 2022.01.20 |