1. 프로퍼티(Property)


우리는 지난 강좌에서 클래스 내부의 필드나 메소드에 마음것 접근하여 호출하고 사용할수 있었습니다.

하지만 이것은 객체 지향 프로그래밍에서 위험한 결과를 초래할수 있습니다.

정보의 은닉은 객체 지향 프로그래밍에서 중요한점인데 외부에서 클래스 내부의 정보를 마음것

수정할수 있다는 것은 버그를 발생하게 하고 안정성을 보장받지 못합니다.


클래스는 수십 수백 개의 필드나 메서드를 가질수 있습니다. 이러한 정보들을 외부에 모두 보여지게

한다면 단순히 혼자 프로젝트를 만드는 것이 아니라 여러명이 한 프로젝트를 만들때 불필요한 정보 노출과

혼선을 야기시킬수 있습니다. 수 많은 필드와 메서드들이 한 곳에 모여서 서로 정보를 노출 시킨다고 상상해

보세요 얼마나 재앙적이고 절망적인 상황이 발생 할지 그래서 외부의 접근을 제한하고 필요한 정보만

외부에 공개할 필요가 있습니다.


결국 우리가 필요한건 정보 은익안전성 확보입니다.


c#에서는 이러한 문제를 해결하기 위해 프로퍼티라는 기능을 제공합니다.

get, set 키워드를 이용하여 필드의 값을 수정할수도 받을수도 있습니다. 반드시 두개를 같이 사용

해야 하는것은 아니고 어느하나만 사용해도 무방합니다.


기본 선언 형식

class 클래스명
{
     [변수 타입] [변수명];
     [접근한정자] [변수 타입] 프로퍼티명
     {
           get
           {
                return [변수명];
            }
           set
            {
                   [변수명] = value;
             }
      }
}

프로퍼티는 필드와 메서드의 기능을 모두 담당 할수있습니다.

예제 코드로 확인 해보도록 하겠습니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        class Test
        {
            private int nNum;

            public int NUMBER
            {
                get
                {
                    return nNum;
                }
                set
                {
                    nNum = value;

                    if (nNum > 100)
                        nNum = 100;
                    else if (nNum < -100)
                        nNum = -100;
                }
            }

            public Test(int a)
            {
                nNum = a;
            }
            public void Show()
            {
                Console.WriteLine("nNum : {0}", nNum);
            }
        }

        static void Main(string[] args)
        {
            Test testA = new Test(88);
            testA.Show();

            testA.NUMBER = 10000;
            testA.Show();

            int nGetNumber = testA.NUMBER;
            Console.WriteLine(nGetNumber);
        }
    }
}

결과

nNum : 88
nNum : 100
100


위의 코드 17줄을 보시면 set의 코드블럭 안에서 100 이상 -100 이하의 값은 받을수 없도록 하여 메서드 처럼

사용하였습니다. 이는 get에서도 가능하며 실제 main 메서드에서 사용한것과 같이 프로퍼티에 변수에 값을

설정하는 것처럼 사용할수도 값을 얻어올수도 있습니다.




2. Class 상속 (virtual, override)


class에서는 상속이라는 기능이 존재 합니다. 쉽게 말해 부모 자식 관계라고 보통 말하는데 

부모 클래스에서 virtual 키워드를 이용하여 이 메서드는 자식 클래스에서 재정의 할수 있도록

하겠다는 것을 의미하고 override 키워드는 자식 클래스에서 virtual로 선언된 메서드를 재정의

하겠다고 알리는 것입니다.


기본 선언 형식

class [부모 클래스명]
{
    virtual [한정자] [반환타입] [메서드명]()
    {
        //
    }
}
class [자식 클래스명] : [부모 클래스명]
{
    override [한정자] [반환타입] [메서드명]()
    {
    }
}


virtual, override 키워드 없이도 상속이 가능합니다.

재정의가 필요한 경우나 재정의가 필요 없는경우도 상속하여 사용 할수 있습니다.

base키워드는 부모의 메서드를 호출할때 사용합니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        class parent
        {
            virtual public void show()
            {
                Console.WriteLine("부모 클래스 show 메서드 호출!");
            }
        }
        class child : parent
        {
            public override void show()
            {
                Console.WriteLine("자식 클래스 show 메서드 호출!");
            }
        }
        class child2 : parent
        {
            override public void show()
            {
                base.show();
            }
        }
        static void Main(string[] args)
        {
            parent parentA = new parent();
            parentA.show();

            child childA = new child();
            childA.show();

            child2 childB = new child2();
            childB.show();
        }
    }
}

결과

부모 클래스 show 메서드 호출!
자식 클래스 show 메서드 호출!
부모 클래스 show 메서드 호출!


위의 코드를 보면 경우에 따라 재정의를 할수도 있고 재정의 하지않고 부모의 메서드를 그대로 사용할수도 있습니다.

또는 부모의 메서드와 같이 사용할수도 있겠지요.

또한 눈치 채셨나요? 앞서 배운 for문이나 whilie문 같이 class역시 중첩하여 다중으로 사용할수 있습니다. 이는 

parent, child, child2 클래스들은 Program클래스 내에서만 사용하기 위해 만든것 입니다. 

외부에서는 절대 저 클래스들이 무엇인지 알수 없게 됩니다.


클래스의 다양성

업캐스팅(Upcasting) : 자식 클래스가 부모 클래스로 형변환

다운캐스팅(Downcasting) : 부모 클래스가 자식 클래스로 형변환


업캐스팅과 다운캐스팅은 잘사용하면 좋은 효과를 얻을수 있지만 명확하지 않은 상황에서는 버그를 발생 시킬수 있습니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        class phone
        {
            virtual public void Call()
            {
                Console.WriteLine("전화를 걸다");
            }
        }
        class android : phone
        {
            public void ApkRead()
            {
                Console.WriteLine("apk 앱설치");
            }
        }
        class iphone : phone
        {
            public void iosRead()
            {
                Console.WriteLine("ios 앱설치");
            }
        }

        static void Main(string[] args)
        {
            android AndroidPhone = new android();
            phone myPhone = AndroidPhone;           //업캐스팅
            ((android)myPhone).ApkRead();           //다운캐스팅

            //예외 발생
            //((iphone)myPhone).iosRead();

            //빌드는 되지만 실행시 예외 발생
            //iphone secendPhone = (iphone)myPhone;  //다운캐스팅
            android secendPhone = (android)myPhone;
            secendPhone.ApkRead();
        }
    }
}

결과

apk 앱설치
apk 앱설치


위의 코드는 개념적으로 안드로이드 폰(자식)을 그냥 폰(부모)이라 부를수 있지만 아이폰이라 부를 수는 없을 것입니다.

또한 폰이라 부르지만 우리는 이것이 안드로이드 폰이라는 것을 알고 있기에 안드로이드 기능(메서드)를 사용함에 있어서도

아무런 문제가 발생되지 않습니다. 하지만 폰을 아이폰 케이스만 입힌다고 아이폰이 될수 없는 것 처럼 아이폰이라 컴파일러를

속일수는 있어도 기능을 사용한다면 예외가 발생합니다.


오버라이딩 봉인

sealed : 파생된 클래스에서 오버라이딩한 메서드에 이 키워드(sealed)를 같이 사용하면 더이상 하위 클래스에서 

           오버라이딩 할수 없습니다.


[virtual] ---> [override sealed] -X-> [override]


...
    virtual public void show()
        {
            Console.WriteLine("부모 클래스 메서드 호출!");
        }
    }
    class child : parent
    {
        override sealed public void show()
        {
            Console.WriteLine("자식 클래스 메서드 호출!");
        }
    }
    class grandchild : child
    {
       override public void show()
        {
            Console.WriteLine("손자 클래스 메서드 호출!");
        }
    }
...


이렇게 컴파일러에서 빌드가 되지 않도록 에러가 발생 됩니다. 왜 이렇게 봉인을 하는것 일까요?

이것은 프로그래머의 배려로 혹시나 오작동을 할 위험이 있는 메서드나 잘못 오버라이딩함으로써 발생할수 있는

오류를 미리 방지하기 위함입니다.


다음 강좌에서는 분할 클래스, 확장 메서드에 대해 알아보도록 하겠습니다.

수고하셨습니다.



+ Recent posts