C# 객체지향 - 다형성과 추상화

선비Sunbei·2023년 1월 9일
1

C#

목록 보기
6/18
post-thumbnail

C# 상속과 생성자

using System;

namespace Study8
{
    class Program
    {
        class Parent
        {
            protected int mVal;
            public Parent()
            {
                mVal = 0;
                Console.WriteLine($"부모 생성자 호출");
            }
            // this 키워드를 이용해서 다른 생성자를 선호출 할 수 있다.
            public Parent(int val) : this(val, 0)
            {
                Console.WriteLine($"다른 부모 생성자를 선 호출");
            }
            public Parent(int val1,int val2)
            {
                this.mVal = val1 + val2;
                Console.WriteLine($"부모 생성자 호출");
            }
        }

        class Child : Parent
        {
            public Child()
            {
                Console.WriteLine($"자식 생성자 호출 | mVale : {mVal}");
            }
            // 부모의 기본 생성자가 아닌 다른 생성자를 base키워드를 이용하여 명시적으로 선택할 수 있다.
            public Child(int val) : base(val) 
            {
                Console.WriteLine($"자식 생성자 호출 | mVale : {mVal}");
            }
        }



        static void Main(string[] args)
        {
            Child child1 = new Child();
            /*
                부모 생성자 호출
                자식 생성자 호출 | mVale : 0
            */
            Child child2 = new Child(20);
            /*
                부모 생성자 호출
                다른 부모 생성자를 선 호출
                자식 생성자 호출 | mVale : 20
             */
        }
    }
}

다른 언어의 객체와 똑같이 자식을 생성하면 자식은 생성되기이전에 부모를 먼저 만들어 생성자를 호출한다.
그런데 가끔 부모 생성자 중 원하는 생성자를 호출하고 싶은 경우가 있다. 이때 사용하는 것이 base라는 키워드이다.
또 다르게 자신의 생성자를 호출하기 이전에 자신의 다른 생성자를 먼저 호출한 후에 처리하겠다면 this 키워드를 이용하면 된다.
이는 C++의 생성자 호출과 비슷하다. ( C++에서는 base,this 키워드가 아닌, 부모나 자신의 클래스 이름을 명시하여 사용)

상속하는 방법은 C++과 유사하다. 다른점이 있다면 C++의 경우 상속할 때도 접근제어자를 통해서 접근에 대한 권한 설정을 할 수 있었지만, C#은 JAVA처럼 extends 키워드를 쓴 것과 동일하다.

C#은 C++과 다르고, JAVA랑 비슷하게 단일상속만이 가능하다. (C++은 다중상속이 가능하다.)

클래스의 캐스팅을 위한 is , as 키워드

using System;

namespace Study9
{
    class Program
    {
        class Parent { }

        class Child : Parent { }
        static void Main(string[] args)
        {
            Parent p = new Child();
            Parent p2 = new Parent();

            Child c = new Child();

            if (p is Child) // p : Child + Parent
                Console.WriteLine("p는 Child입니다."); // o
            else
                Console.WriteLine("p는 Child가 아닙니다.");

            if (p2 is Child) // p2 : Parent
                Console.WriteLine("p2는 Child입니다.");
            else
                Console.WriteLine("p2는 Child가 아닙니다."); // o

            if(c is Parent) // c : Child + Parent
                Console.WriteLine("c는 Parent입니다."); // o
            else
                Console.WriteLine("c는 Parent가 아닙니다.");

            Child c2 = p as Child; // p : Child + Parent
            if(c2 is Child)
                Console.WriteLine("c2는 Child입니다."); // o
            else
                Console.WriteLine("c2는 Child가 아닙니다.(NULL)");

            Child c3 = p2 as Child; // p2 : Parent
            if (c3 is Child)
                Console.WriteLine("c3는 Child입니다.");
            else
                Console.WriteLine("c3는 Child가 아닙니다.(NULL)"); // o

            Parent p3 = c2 as Parent; // p3 : Parent, c2 : Child + Parent
            if (p3 is Child)
                Console.WriteLine("p3는 Parent입니다."); // o
            else
                Console.WriteLine("p3는 Parent가 아닙니다.");
        }
    }
}

is 키워드는 해당 데이터가 우항에 기술 된 클래스이거나 포함하고 있으면 true를 반환하고, 만약 포함하지않을 경우 false를 반환한다.
as 키워드는 형식 변환 연산 키워드로 캐스팅 연산과 다른 점은 변환이 가능하지 않는 경우 예외를 발생시키지 않고 null로 반환한다.

가상함수

using System;

namespace Study10
{
    class Program
    {
        class Parent
        {
            public virtual void Read()
            {
                Console.WriteLine("부모는 책 읽는 중");
            }
        }
        class Child1 : Parent
        {
            public override void Read()
            {
                Console.WriteLine("첫째는 책 읽는 중");
            }
        }
        class Child2 : Parent
        {
            public override void Read()
            {
                Console.WriteLine("둘째는 책 읽는 중");
            }
        }
        static void Main(string[] args)
        {
            Parent p = new Parent();
            p.Read(); // 부모는 책 읽는 중
            p = new Child1();
            p.Read(); // 첫째는 책 읽는 중
            p = new Child2();
            p.Read(); // 둘째는 책 읽는 중
        }
    }
}

다형성(polymorphism)이란? 다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미합니다.
이처럼 가상함수를 이용하면 하나의 함수로도 각각의 자식마다 하는 행동이 달라집니다.

순수가상함수

using System;

namespace Study10
{
    class Program
    {
        abstract class Person 
        {
            public abstract void Read();
        }
        class Child1 : Person
        {
            public override void Read()
            {
                Console.WriteLine("첫째는 책 읽는 중");
            }
        }
        class Child2 : Person
        {
            public override void Read()
            {
                Console.WriteLine("둘째는 책 읽는 중");
            }
        }
        static void Main(string[] args)
        {
            Person p = new Child1();
            Person p2 = new Child2();
            p.Read();
            p2.Read();
        }
    }
}

다음은 순수 가상함수를 사용하는 방법이다.
C++에서는 virtual 키워드를 쓰고 함수() = 0 을 하면 자동으로 해당 클래스가 순수 가상함수가 되었다.
이에반해 C#은 JAVA처럼 abstract 키워드를 이용해서 추상클래스를 만든다.
추상클래스는 일반 클래스의 방식과 후에 기술 될 interface 기술이 섞여있다보니까 다중 상속이 제한된다.

interface(추상화)

using System;

namespace Study10
{
    class Program
    {

        interface IWalk
        {
            public void Walk();
        }
        interface IEat
        {
            public void Eat();
        }
        interface IAttack
        {
            public void Attack();
        }
        abstract class Person : IWalk, IEat
        {
            public abstract void Read();

            public void Walk()
            {
                Console.WriteLine("걷는 중이다.");
            }
            public void Eat()
            {
                Console.WriteLine("밥을 먹는다.");
            }
        }
        class Child1 : Person
        {
            public override void Read()
            {
                Console.WriteLine("첫째는 책 읽는 중");
            }
        }
        class Child2 : Person, IAttack
        {
            public void Attack()
            {
                Console.WriteLine("공격을 했다.");
            }

            public override void Read()
            {
                Console.WriteLine("둘째는 책 읽는 중");
            }
        }
        static void Main(string[] args)
        {
            Person p = new Child2();

            p.Read();
            p.Walk();
            p.Eat();

            IAttack p_Attack = p as IAttack;
            p2_Attack.Attack();
        }
    }
}

Interface는 abstract class와 비슷하지만 데이터를 담을 수 없다.
또 class의 경우 단일 상속만 가능하지만, Interface의 경우 다중상속이 가능하다.

            IAttack p_Attack = p as IAttack;
            p2_Attack.Attack();

사용 방법은 다음과 같이 사용할 수 있다. 물론 null 체크를 꼭 해야 한다.

0개의 댓글