본문 바로가기

C# 일기

7. 프로퍼티(Property)

우리가 객체지향 프로그래밍 언어인 C#을 통해 코딩하면서, 클래스의 사용은 불가피하다.

 

그리고 객체지향에서는 정보은닉이 굉장히 중요하다.

 

객체지향에서는 여러 객체들이 서로의 정보를 공유하면서 그 상호작용을 통해 프로그램을 완성시키는 것이기 때문에

상호간의 데이터 공유가 활발해지면서 생기는 메모리 침범현상등의 문제가 발생할 수 있다.

 

예를 들면, 접근해야 할 값이 아닌 곳에 접근하여 사용자가 식별할 수 없는 임의의 공간에 있는 임의의 자료의 값을 수정하는 등의 문제는, 고치는 것은 물론이고, 어디에서 문제가 발생했는지를 찾는 것 조차 어려워진다.

 

이렇기 때문에 우리가 class를 통해 객체의 정보를 꾸릴 때는 정보의 은닉이 필수불가결하다.

 

그러면 어떻게 객체의 정보를 은닉하는 것이 좋을까?

 

우리가 객체의 특징을 채워넣을 때의 유의해야하는 사항이 무엇일까?

 

 

 

 

 

가장 중요한 것은, 특별한 이유를 제외하고는 필드, 즉 멤버 변수를 public으로 선언해선 안된다.

 

멤버에 직접 접근하여 값을 바꾸는 행위는 마치 금고를 밖에 비밀번호도 걸어놓지 않은 채 내놓고 마음대로 하라는 것과 같다.

 

혹은 다른 사람의 정보나 소지품을 다른 사람이 마음대로 바꾸거나 손을 대는 것이다.

 

정리하자면, 정말 특별한 사유가 존재하지 않는 이상, 멤버에의 직접접근은 금기시 된다는 것이다.

 

그렇기 때문에 필드의 값에 접근을 하는 것은 직접 접근이 아닌, 접근자(Getter)와 설정자(Setter)를 통해 간접적으로 접근하여 값을 수정해야 했다.

 

이 원칙을 수행하기 위해서 우리는 앞서 말했던 것 처럼 GetOO, SetOO라는 함수를 통해서 외부와 내부를 연결지어줬었다.

 

 

using System;

namespace Favor
{
   class Circle
   {
      public void SetRadius(int value) { radius = value; }
      public int GetRadius() { return radius; }
      
      int radius;
   }
   internal class Program
   {
      static void Main(string[] args)
      {
         Circle c = new Circle();
         c.radius = 10; // 런타임 에러.
         c.SetRadius = 10;
         Console.WriteLine(c.GetRadius);
         // 출력값 : 10
      }
   }
}

위와 같이 public인 접근자와 설정자를 통해 내부의 값을 간접적으로 바꾸어주는 것이 바람직하다.

 

그런데 이처럼 임의의 함수 두개를 만들어서 접근하는 것 보다 가독성이 좋으며 편리한 기능이 있는데, 이는 프로퍼티를 사용하는 것이다.

 

 

 

프로퍼티란, 접근자와 설정자의 기능을 동시에 갖고있다.

 

프로퍼티의 모양새는 매개변수가 없는 함수와 비슷하다. 매개변수가 () 비어있는 것이 아닌, 매개변수를 입력할 소괄호 조차 갖고있지 않다.

 

따라서 언뜻 보기엔 변수인 것 같기도, 함수인 것 같기도 하다.

 

이러한 생김새 때문에 컴파일러는 프로퍼티를 보면 자동으로 이녀석이 프로퍼티인지를 알 수 있다.

 

프로퍼티의 사용법은 아래와 같다.

 

 

 

using System;

namespace Favor
{
   class Circle
   {
      public int Radius 
      {
         get
         {
            return radius;
         }
         set 
         {
            radius = value; 
         }
      }
      
      int radius;
   }
  internal class Program
  {   
     static void Main(string[] args)
     {
        Circle c = new Circle();
        c.Radius = 10;
        Console.WriteLine(c.Radius);
        // 출력값 : 10
     }
  }   
}

 

 

우선 접근제한자로 접근 허용 범위를 정하고, 다룰 자료형을 선언한 후, 프로퍼티의 이름을 짓는다.

 

그 이후의 get과 set부분을 설명하기 전에, get과 set이 언제 호출되는지에 대해 설명하고 넘어가겠다.

 

 

 

먼저 set은 이 프로퍼티에 값을 대입할 경우 호출된다.

 

우리가 설정자(Setter)를 통해 클래스 내부의 값에 접근, 클래스의 멤버를 변경할 때 사용했던 것 처럼, 프로퍼티를 통해 값의 변경을 시도할 때 호출된다.

 

즉, 위 코드의 메인함수에서 c.Radius = 10; 을 통해 radius의 값을 변경하려고 할 때 호출된다.

 

 

 

get은 이 프로퍼티에 설정된 내부 멤버에 접근하는 경우에 호출된다.

 

우리가 접근자(Getter)를 통해 클래스 내부의 값에 접근하여 값을 얻어올 때와 같이, 값의 수정이 아닌 접근에 한한다.

 

즉, 아래 메인 함수에서 get이 호출된 부분은 Console.WriteLine(c.Radius); 이고, c.Radius를 통해 접근하여 10을 출력했을 때 get은 호출된다.

 

 

 

그럼 get의 내용에 대해 이해할 수 있다. 값을 가져오려고 했기 때문에, 내부의 멤버인 radius를 리턴해주면, get을 호출했을 때 radius의 값을 가져올 수있으므로 위와같이 작성했다.

 

set도 마찬가지로 내부의 멤버에 접근하여 값을 수정하는 내용을 작성하면 되는데, 여기서 문제가 발생한다.

프로퍼티는 매개변수가 없기 때문이다.

 

매개변수가 없는 프로퍼티는 대입하려는 값(c.Radius = 10에서 10이 이에 해당된다.)에 접근할 수 없다.

 

그렇기 때문에 set코드 내에서 value라는 예약어를 설정해서, 대입하려는 값을 value로 대신해서 받기로 약속한 것이다.

 

그러면 이제 set의 내용을 이해할 수 있다.

 

set이 호출되면, 대입연산자의 우항에 있는 값 10을, 매개로 전달받는 것이 아닌 value라는 예약어를 통해 받아서, 그 값을 내부의 멤버인 int radius에 대입하여 내부 멤버의 값을 설정할 수 있다.

 

 

 

 

 

프로퍼티를 이용하면 따로 접근자와 설정자를 만들지 않아도, 프로퍼티 하나로 모든 일이 해결된다.

 

또한, 프로퍼티의 내용을 위에 작성한 것과 같이 사용자가 직접 설정할 수 있지만, 특별한 예외 사항이 없으면 자동으로 설정하는 것도 가능하다. 

 

 

 

class Circle
{
   public int Radius { get; set; }
}

세로로 길게 늘어져있던 프로퍼티가 드라마틱하게 짧은 모양새가 됐다.

 

이상한 점을 발견했는데, 바로 프로퍼티에 해당하는 변수가 존재하지 않는다는 것이다.

 

이 자동설정 프로퍼티는 Radius 프로퍼티를 변수처럼 사용하는 것을 가능하게 만들어준다.

 

다시말해, 위처럼 프로퍼티를 선언하면 컴파일러가 자동으로 임의의 변수를 선언하여 주고, 이 변수를 사용한 get과 set 프로퍼티를 만들어준다.

 

마찬가지로 자동 설정된 프로퍼티는 set을 통해 Radius의 값을 설정하고, get을 통해 Radius의 값을 불러올 수 있다.

 

 

 

 

 

 

개인적으로 굉장히 유용하다고 생각하는 프로퍼티에 대해서 알아보았다.

 

정보은닉원칙을 철저히 수행하면서, 클래스 내부의 멤버를 마치 변수처럼 접근하고, 설정할 수 있다는 점에서 굉장히 유용하다.

 

이를 통하면 따로 접근자와 설정자를 호출할 필요도, 선언할 필요조차도 없어져 편의성에 있어 굉장히 강한 것 같다.