언어 자료구조 알고리즘/Escort C#

[C#] 8. 인터페이스와 컬렉션 - 인터페이스

언제나휴일 2016. 5. 3. 15:54
반응형

8. 인터페이스와 컬렉션

 

 C#에서는 인터페이스를 통해 기능 구현에 대한 약속을 추상화할 수 있습니다. 인터페이스는 묵시적으로 추상 형식이며 클래스나 구조체에서 이를 구현 약속(상속)하면 약속된 기능들을 구현해야 합니다.

 

 이러한 인터페이스를 이용하면 같은 인터페이스 기반의 여러 개체를 인터페이스 형식 변수로 사용하는 다형성의 장점을 누릴 수 있습니다. 또한, 이를 프로그래밍에 사용하면 같은 인터페이스를 구현 약속된 개체의 사용 방법을 별도로 익힐 필요가 없게 됩니다.

 

 그리고 C#에서는 데이터나 개체를 보관할 수 있는 여러 종류의 컬렉션을 제공하고 있으며 필수적인 인터페이스 기반으로 정의되어 있어서 효과적으로 프로그래밍할 수 있습니다.

 

8.1 인터페이스

 

 인터페이스는 기능 구현에 대한 약속입니다. 인터페이스의 멤버로는 메서드와 속성, 이벤트, 인덱서를 캡슐화할 수 있으며 인터페이스 내부에서는 구체적인 구현을 하지 않습니다. 대신 인터페이스를 구현 약속하는 클래스나 구조체에서는 해당 인터페이스에 약속된 모든 멤버를 구체적으로 구현을 제공해야 합니다.

 

 이와 같은 인터페이스를 사용하면 비슷한 기능이 필요한 여러 형식의 개체를 같은 방법으로 사용할 수 있는 편의성을 제공할 수 있습니다. 또한, 특정 인터페이스를 기반의 개체를 인터페이스 형식 변수로 인자를 전달받아 처리함으로써 인터페이스에 약속된 기능만 접근할 수 있게 할 수 있어 신뢰성도 높아집니다.

 

 이 외에도 C#에서는 여러 인터페이스를 구현 약속이 가능하여 효과적으로 프로그래밍할 수 있습니다.

 

8.1.1 인터페이스 정의 및 구현 약속

 

 인터페이스를 정의할 때는 멤버를 구체적인 구현하지 않고 캡슐화해야 합니다. 그리고 각 멤버는 묵시적으로 접근 한정이 public이며 접근 한정자를 지정할 수 없습니다.

 

▶ 인터페이스 정의

interface IStudy

{

    void Study(); //접근 한정자를 지정할 수 없음

}

 

 그리고 정의된 인터페이스를 구현 약속하는 경우에는 파생과 같은 방법으로 구현 약속할 인터페이스를 지정할 수 있습니다. 그리고, 구현 약속한 인터페이스에 멤버들은 구체적으로 구현해야 합니다. 인터페이스 기반의 형식에서 약속한 멤버를 구현하는 방법에는 묵시적 인터페이스 구현과 명시적 인터페이스 구현이 있습니다. 묵시적 인터페이스 구현에서는 약속된 멤버와 같은 시그니쳐를 갖게 선언하고 public으로 접근 지정해야 합니다.

 

▶ 묵시적 인터페이스 구현

class Student : IStudy

{

    public void Study() //public으로 접근 지정해야 함

    {

    }

}

 

 명시적 인터페이스 구현에서는 인터페이스 이름을 선언부에 명시하며 접근 한정자를 명시하지 않습니다.

 

▶ 명시적 인터페이스 구현

class Student : IStudy

{

    IStudy.Study()

    {

    }

}

8.1.2 다중 인터페이스 구현 약속

 

 C#에서는 기반 클래스는 하나밖에 지정하지 못하지만 기반 인터페이스는 여러 개를 지정할 수 있습니다.

 

▶ 명시적 인터페이스 구현

interface IStudy

{

    void Study();

}

interface ISleep

{

    void Sleep();

}

class Student : IStudy, ISleep

{

    public void Study()

    {

        Console.WriteLine("학생이 공부하다.");

    }

    public void Sleep()

    {

        Console.WriteLine("학생이 잠을 자다.");

    }

}

 

 만약, 기반 클래스에서 파생도 받아야 하고 인터페이스 구현 약속도 해야 한다면 기반 클래스 이름을 맨 앞에 명시하여야 합니다.

 

▶ 명시적 인터페이스 구현

class Man

{

    public void Think()

    {

        Console.WriteLine("생각하다.");

    }

}

interface IStudy

{

    void Study();

}

class Student : Man,IStudy

{

    void IStudy.Study()

    {

        Console.WriteLine("학생이 공부하다. ");

    }

}

 

8.1.3 명시적 인터페이스 구현

 

 같은 이름의 멤버가 캡슐화되어 있는 서로 다른 인터페이스를 기반으로 형식을 정의할 때는 묵시적 인터페이스 구현을 하면 이름 충돌이 납니다. 이럴 때는 명시적 인터페이스 구현으로 이름 충돌을 피해야 합니다.

 

▶ 명시적 인터페이스 구현으로 이름 충돌

interface IStudy

{

    void Study();

    void Work(); //ITeach 인터페이스에도 같은 이름의 멤버가 있음

}

interface ITeach

{

    void Teach();

    void Work(); //IStudy 인터페이스에도 같은 이름의 멤버가 있음

}

class PartTimeTeacher : IStudy, ITeach

{

    void IStudy.Study()

    {

        Console.WriteLine("공부하다.");

    }

    void IStudy.Work() //이름 충돌을 막기 위해 명시적 인터페이스 구현

    {

        Console.WriteLine("강의를 듣다.");

    }

    void ITeach.Teach()

    {

        Console.WriteLine("강의하다.");

    }

    void ITeach.Work() //이름 충돌을 막기 위해 명시적 인터페이스 구현

    {

        Console.WriteLine("연구하다.");

    }

}

 이처럼 명시적으로 인터페이스 구현하면 사용하는 곳에서는 인터페이스 형식 변수로 개체를 참조해야 약속된 멤버를 사용할 수 있습니다.

 

▶ 명시적 인터페이스 구현으로 이름 충돌

class Program

{

    static void Main(string[] args)

    {

        PartTimeTeacher pt_teacher = new PartTimeTeacher();

        IStudy istudy = pt_teacher as IStudy; //IStudy 인터페이스 참조

        if (istudy != null)

        {

            istudy.Study();

        }

    }

}


 [그림 43]은 명시적 인터페이스 구현한 개체 멤버를 직접 접근할 때 오류 화면입니다.


명시적 인터페이스 구현한 개체 멤버를 직접 접근할 때 오류 화면

 

[그림 43] 명시적 인터페이스 구현한 개체 멤버를 직접 접근할 때 오류 화면


8.1.4 인터페이스에 캡슐화 가능한 멤버

 

 인터페이스에는 메서드 외에도 속성, 이벤트, 인덱서를 멤버로 캡슐화할 수 있습니다. 속성과 인덱서는 get블록과 set블록은 원하는 블록만 것을 선택적으로 약속할 수 있으며 이벤트에 대해서는 9장 대리자와 이벤트에서 다루기로 하겠습니다.

 

 다음은 인터페이스에 멤버 속성과 멤버 인덱서를 캡슐화하는 예제 코드입니다. 인터페이스 IMemo에서는 기록할 수 있는 메모의 최대 개수를 가져올 수 있는 MaxCount 속성과 원하는 위치에 메모를 설정하거나 가져올 수 있는 인덱스를 약속하였습니다.

 

▶ 인터페이스에 멤버 속성과 인덱스를 캡슐화

interface IMemo

{

    int MaxCount {    get;    }

    string this[int index] {    get;    set;    }

}

 

 이번에는 IMemo를 구현 약속하는 Note 클래스를 만듭시다. Note 클래스에는 IMemo에서 약속된 멤버들을 구체적으로 구현해야 합니다.

 

▶ 속성과 인덱스를 캡슐화한 인터페이스를 구현 약속

class Note : IMemo

{

    public int MaxCount

    {

        get { }

    }

    public string this[int index]

    {

        get{    }

        set{    }

    }

}

 

 Note 클래스 생성자에서는 노트의 페이지 수를 입력 인자로 받아 최대 보관할 수 있는 메모의 개수를 정하고 메모를 남길 수 있는 페이지들을 생성합시다. 이를 위해 MaxCount set 블록을 추가하고 메모를 남길 수 있는 페이지를 위해 문자열 배열을 멤버로 추가할게요. 물론, set 블록은 노트 내부에서만 접근할 수 있게 private으로 접근 지정해야겠지요. 그리고 인덱서에서는 접근하려고 하는 index가 유효하면 남겨진 메모를 설정하거나 가져오기를 할 수 있게 구현합시다.

 

Note 클래스 구현

class Note : IMemo

{

    string[] pages = null;

 

    public int MaxCount  //IMemo에서 약속한 속성 구현

    {

        get; //IMemo에서 약속한 블록

        private set; //IMemo에서 약속하지 않은 블록이지만 추가했음

    }

    public string this[int index] //IMemo에서 약속한 인덱서 구현

    {

        get

        {

            if (AvailIndex(index))

            {

                return pages[index];

            }

            return null;

        }

        set

        {

            if (AvailIndex(index))

            {

                pages[index] = value;

            }

        }

    }

    public Note(int max_memo)

    {

        MaxCount = max_memo;

        pages = new string[max_memo];

    }

    private bool AvailIndex(int index)

    {

        return (index >= 0) && (index < MaxCount);

    }

}

 

 다음은 Note 클래스를 사용하는 간단한 예제입니다.

 

Note 클래스 사용 예

class Program

{

    static void Main(string[] args)

    {

        Note note = new Note(3);

        Console.WriteLine("최대 {0}개의 메모를 남길 수 있습니다. ", note.MaxCount);

        note[0]="1시 강의";

        note[1]="5시 미팅";

        Console.WriteLine("메모 리스트");

        for (int i = 0; i < note.MaxCount; i++)

        {

            Console.WriteLine("{0}:{1}", i + 1, note[i]);

        }

    }

}

▶ 실행 결과

최대 3개의 메모를 남길 수 있습니다.

1:1시 강의

2:5시 미팅

3:

 

너와 나의 연결고리 "공감"

반응형