본문 바로가기

프로그램&DB/C#

[C# 강좌] C# 프로그래밍 #09- 상속

3.2 상속

3.2.1 상속이란?

상속이란 클래스를 만들 때, 이전에 만들어진 클래스의 기능을 가져와 클래스를 구성하거나 사용하기 위해 사용되는 객체지향프로그래밍 기법의 하나이다. C#에서 상속은 C++와는 약간의 차이가 있는데, 그 중 하나는 클래스 다중 상속을 지원하지 않는 다는 것이다.

승용차, SUV차량 2개의 자동차에 대한 프로그램을 만든다고 가정하자. 2가지 클래스의 공통점은 자동차라는 것이다. 또한, 기능적인 면에서 거의 일치하며, 다만 약간씩의 차이가 있을 뿐이다. 이것을 어떻게 구현할 것인가?

유사한 기능들을 승용차에 구현하고 또 SUV차량에 구현하는 것은 구조적으로 좋지 못하다.

1) 기능을 업데이트 할 때 2가지 차량에 대한 모든 기능을 수정해야 한다.

2) 차종이 많아지면 차종에 따른 클래스를 모두 만들어야 한다.

 

상속은 클래스 이름 뒤에 콜론(:)을 붙이고 상속 받을 부모 클래스명을 넣게 된다.

/액세스 한정자/  class [자식 클래스 이름] : [부모 클래스 이름]

 

다음 예제는 상속이라는 방법을 통해 차량의 기능을 구현해 보도록 하겠다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

namespace Inheritance_Sample1

{

    class Program

    {

        static void Main(string[] args)

        {

            Automobile myCar1 = new Automobile();

            Console.WriteLine(myCar1.GetName());

            myCar1.doStart();

            myCar1.doStop();

 

            SUV myCar2 = new SUV();

            Console.WriteLine(myCar2.GetName());

            myCar2.doStart();

            myCar2.doStop();

 

            Console.ReadLine();

        }

    }

 

    public class Automobile : Car

    {

        public Automobile()

        {

            CarName = "승용차";

        }

    }

 

    public class SUV : Car

    {

        public SUV()

        {

            CarName = "SUV";

        }

    }

 

    public class Car

    {

        protected bool IsEngineOn;

        protected bool EngineBreak;

        protected int SteeringWheel;

        protected string CarName = "";

 

        public Car()

        {

        }

 

        public void doStart()

        {

            Console.WriteLine("출발");

        }

 

        public void doStop()

        {

            Console.WriteLine("정지");

        }

 

        public string GetName()

        {

            return CarName;

        }

    }

}

 

21번째 줄의 승용차(Automobile) 29번째 줄의 SUV차량은 모두 Car로부터 상속을 받고 있다. 따라서 승용차와 SUV 차량은 모두 Car의 멤버 변수와 doStart, doStop의 멤버 메서드를 그대로 가진다고 보면 된다. 실제 승용차 클래스와 SUV 클래스는 생성자에서 CarName 변수 하나만을 셋팅하고 있지만, Main 메서드에서 보면 둘다 doStart doStop 메서드를 이용하는 것을 볼 수 있다. 이와 같이 부모 클래스의 멤버 변수와 멤버 메서드를 활용 가능하도록 해주는 것을 상속이라고 보면 된다.

상속은 받은 자식 클래스는 부모 클래스의 멤버들의 접근 권한에 따라 부모 클래스에 대해 접근이 가능하게 된다.

 

3.2.2 생성자 재정의

 

 

3.2.3 상속과 재정의(Override)

클래스 상속 시에 기존에 정의된 부모 클래스의 작동이 현재 클래스에서는 달라져야 하거나, 부모 클래스가 추상 클래스인 경우 멤버 메서드들에 대한 재정의를 해야 한다. 이를 메서드 재정의(override)라고 부른다. 기존에 부모 클래스의 메서드에 대해 재정의 한다고 명시적으로 알려주기 위해 override 키워드를 이용한다.

 

1) 재정의 가능한 메서드

재정의 가능한 메서드는 virtual 로 선언되어 있거나, abstract로 선언되어 있는 경우 자식 클래스에서 재정의 가능하다. Virtual abstract의 차이는 abstract의 경우 자식 클래스에서 무조건 재정의를 해주어야 하지만, virtual의 경우는 필요할 경우 재정의를 해줄 수 있다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

namespace Override_Sample1

{

    class Program

    {

        static void Main(string[] args)

        {

            MyCar car = new MyCar();

            car.doStart();

            car.doStop();

 

            Console.ReadLine();

        }

    }

 

    public class MyCar : Car

    {

        public void doStop()

        {

base.doStop();

            Console.WriteLine("Override doStop");

        }

    }

 

    public class Car

    {

        protected bool IsEngineOn;

        protected bool EngineBreak;

        protected int SteeringWheel;

 

        public Car()

        {

        }

 

        public virtual void doStart()

        {

            Console.WriteLine("doStart");

        }

 

        public virtual void doStop()

        {

            Console.WriteLine("Override doStop");

        }

    }

}

 

위의 소스에서 Car 클래스의 doStart doStop메서드는 virtual로 선언되었다. 그리고, 15번째 줄의 MyCar 클래스에서 doStop가 재정의되고 있는 것을 볼 수 있다. 이와 같이 virtual 로 선언된 경우 재정의 할 수 있고 하지 않아도 아무런 문제가 없다.

부모 클래스가 abstract이고 abstract인 메서드가 존재할 경우 상속 받는 자식 클래스는 abstract 메서드를 무조건 재정의 해주어야 한다.(추상 클래스 참조)

 

2) override를 통한 재정의와 new를 통한 재정의

 

 

 

 

3.2.4 메서드 오버로딩(Overloading)

메서드는 메서드 이름과 매개변수 목록 반환 형식으로 이루어진다. 메서드 오버로딩은 동일한 이름의 메서드에 매개 변수를 다르게 하여 여러 개의 메서드로 정의하는 것을 의미한다.

메서드를 오버로딩 하는 이유는 주로 메서드에 여러 타입의 데이터를 처리할 수 있도록 하기 위해 이용된다. 다음 소스를 보자.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

namespace Inheritance_Sample1

{

    class Program

    {

        static void Main(string[] args)

        {

            char num1 = '1';

            string num2 = "123";

 

            if (new Program().IsDigit(num1)) Console.WriteLine("숫자");

            else Console.WriteLine("숫자 아님");

 

            if (new Program().IsDigit(num2)) Console.WriteLine("숫자");

            else Console.WriteLine("숫자 아님");

 

            Console.ReadLine();

        }

 

        public bool IsDigit(char target)

        {

            return Char.IsDigit(target);

        }

 

        public bool IsDigit(string target)

        {

            for (int i = 0; i < target.Length; i++)

            {

                if (IsDigit(target[i])) continue;

                else return false;

            }

return true;

        }

    }

}

 

이 소스에는 IsDigit라는 이름의 메서드가 매개 변수 타입이 하나는 char(19번째 줄)이고 하나는 string(24번째 줄)으로 2개 선언되어 있다. 이와 같이 메서드 이름이 동일하며, 매개 변수만 다른 경우를 메서드 오버로딩이라고 부른다는 걸 위에서 설명하였다. 여기에 이용된 IsDigit 메서드는 입력된 값이 숫자인지 판별해주는 메서드로, 2개를 만든 이유는 문자로 입력된 매개 변수와 문자열로 입력된 매개 변수를 둘 다 처리해줄 수 있도록 하기 위해서이다.

24번째 줄의 IsDigit 메서드의 매개변수를 char 형으로 수정하게 되면 동일한 매개 변수 형식을 가지는 멤버가 미리 정의되어 있다고 컴파일 오류가 발생하게 된다.

 

3.2.6 추상 클래스

추상 클래스는 클래스를 설계하는데 있어서 기본 개념만을 담고 있는 미완성의 클래스라고 볼 수 있다. 추상 클래스는 일반 클래스 작성과 마찬가지로 멤버를 정의하고 메서드를 만들게 되지만, 추상 클래스만의 특징은 구현을 가지지 않는 추상 메서드를 가질 수 있다는 것이다. 그럼 왜 추상 메서드를 가지는 추상 클래스를 굳이 만들어 사용하는가?

클래스라는 개념이 들어가 있는 이상, 데이터 추상화와 상속이라는 개념을 따로 때어 놓고는 갈수가 없을 것이다. 추상 클래스를 사용하는 이유는 메서드 추상화와 자식 클래스들에 대한 메서드 구현을 강요하기 위한 의미로 보면 된다.

경유차, 가솔린차, LPG가스차 라는 3가지의 자동차 클래스가 있을 때, 시동을 건다던지, 브레이크를 밟는다던지, 라이트를 켠다던지와 같은 몇 가지 동일한 행위를 하는 역할을 가정해 보자. 이 기능들을 처리하기 위해 각각의 클래스에서 구현하기 보다는 Car이라는 클래스를 만들어 기능들에 대한 메서드를 구현하고 이를 상속 받는 클래스 경유차, 가솔린차, LPG가스차를 만들어 사용하면 부모 클래스의 메서드를 가져와 그대로 이용할 수도 있다. 그러나 동일한 행위를 하지만 내부적으로 그 행위를 하기 위해 다른 행위들을 해야 하는 경우가 있을 수 있다. 여기서는 엔진에 공급하는 연료가 다 다르므로 처리가 틀려져야 할 수 있다. 이 경우 행위에 대한 이름만 정의하고 각각 상속받은 클래스에서 구현해야 한다. 이때 사용하는 것이 추상 메서드라는 것이다. 사용자에게 이 부분은 따로 구현해야 한다는 것을 강제할 수 있다는 것이다.

추상 클래스는 abstract 키워드를 이용해서 만들 수 있으며 그 형식은 다음과 같다.

/액세스 한정자/  abstract class [클래스 이름]

 

abstract 한정자를 이용해 클래스를 구현하게 되면, 그 클래스는 인스턴스화 해서 사용할 수 없고 그 클래스를 상속받아 구현해서 이용해야 한다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

using System;

using System.Collections.Generic;

using System.Text;

 

namespace Method_Sample1

{

    class Program

    {       

        static void Main(string[] args)

        {

            LPGCar lpgCar = new LPGCar();

           

            Console.ReadLine();

        }

    }

 

    public class LPGCar : Car

    {

 

        public override void doStart()

        {

            this.IsEngineOn = true;

        }

 

        public override void doStop()

        {

            this.IsEngineOn = false;

        }

    }

 

    public abstract class Car

    {

        protected bool IsEngineOn;

        protected bool EngineBreak;

        protected int SteeringWheel;

       

        public Car()

        {

        }

 

        public abstract void doStart();

        public abstract void doStop();

       

    }

}

 

 

3.2.5 인터페이스 상속

인터페이스는 구현을 갖지 않는 껍데기를 만들기 위해 이용된다. C# C++의 다중 클래스 상속과 같은 기능을 지원하지 않는다. 상속은 오직 클래스 하나만을 상속 받을 수 밖에 없다. 따라서, 여러 가지 특성을 같이 가져야 하는 클래스에 대해서는 각각의 특성의 종류를 묶어 인터페이스들을 만들고 다중 인터페이스 상속을 이용할 수 있다. 다음 소스는 인터페이스 상속을 통해 Excavator(굴삭기) 클래스에 대한 구현을 해 본 예제이다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

namespace Interface_Sample1

{

    public class Excavator : Car, ICrane, IBulldozer

    {

        public override void doStart()

        {

            this.IsEngineOn = true;

        }

 

        public override void doStop()

        {

            this.IsEngineOn = false;

        }

 

        public void doLift()

        {

        }

 

        public void doBulldoze()

        {

        }

    }

 

    public class Car

    {

        protected bool IsEngineOn;

        protected bool EngineBreak;

        protected int SteeringWheel;

 

        public Car()

        {

        }

 

        public virtual void doStart()

        {

        }

 

        public virtual void doStop()

        {

        }

    }

 

    public interface ICrane

    {

        void doLift();

    }

 

    public interface IBulldozer

    {

        void doBulldoze();

    }

}

 

 

다음 예제는 인터페이스 상속을 통해 구현된 클래스들을 인터페이스 타입으로 조건에 따라 불러오는 예제이다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

namespace Interface_Sample2

{

    class Program

    {

        static void Main(string[] args)

        {

            string _line = System.Console.ReadLine();

 

            IGame myGame;

            if (_line == "1") myGame = new Card();

            else if (_line == "2") myGame = new Gostop();

            else myGame = new DifImage();

 

            Console.WriteLine(myGame.SelectGame());

 

            Console.Read();

        }

    }

 

    public class Card : IGame

    {

        public string SelectGame()

        {

            return "카드";

        }

    }

 

    public class Gostop : IGame

    {

        public string SelectGame()

        {

            return "고스톱";

        }

    }

 

    public class DifImage : IGame

    {

        public string SelectGame()

        {

            return "숨은그림찾기";

        }

    }

 

    public interface IGame

    {

        string SelectGame();

    }

}

 



/* 출처 */
inobae의 놀이터
http://blog.naver.com/inobae