[C# 기초강좌] 12. C# 인덱서와 프로퍼티

Posted in SW개발 // Posted at 2023. 11. 6. 13:50
728x90
이 글은 제가 과거에 운영했던 사이트인 http://dotnet.mkexdev.net 의 글을 옮겨온 것입니다. 원본 글은 2010년에 작성되었습니다.

그 전에 운영했었던 사이트(mkex.pe.kr)은 흔적도 없이 사라 졌습니다. 그속의 글들도 모두... 그래서 이 사이트도 사라지기 전에 옮기고 싶은 글을 조금씩 이 블로그로 이동시키려 합니다.
(원본글) http://dotnet.mkexdev.net/Article/Content.aspx?parentCategoryID=1&categoryID=5&ID=676

이 글은 닷넷 기초 지식을 전파하기 위해 2010경에 작성되었으며, 당시 윤성우의 프로그래밍 스터디그룹 네이버 카페에도 필진으로 참여하여 연재했던 글이기도 합니다. 현재 시점에서 조금 달라진 부분이 있을 수 있으나 기본 원리와 언어 기초에 해당하는 부분은 크게 변하지 않았을 것으로 생각하며 이런 부분들을 감안해서 봐 주시기 바랍니다.

 

인덱서(Indexer) 와 프로퍼티(Property) 는 결국 메서드 입니다

 

안녕하세요. 박종명입니다. 닷넷 열두 번째 강좌를 진행하도록 하겠습니다.
이번 강좌는 C# 인덱서 프로퍼티라는 언어적 특징을 소개 합니다.

인덱서와 프로퍼티는 언어적 편리성을 제공하기 위한 C#의 기능이며 이 둘은 결국 메서드의 조금 다른 표현이라
할 수 있습니다.


 C#은 개발자를 위해 많은 언어적 기능을 제공하는데요.

인덱스 프로퍼티도 이러한 기능의 일부 입니다. 이 둘의 사용형태는 조금 다르지만 많은 유사성이 있습니다.

그리고 사용법이 간단하여 같이 묶어 설명 드리겠습니다. 그럼 하나씩 살펴 보도록 합니다

 

 

프로퍼티(Property)

 

Property  사물의 속성이라는 뜻을 가지고 있습니다. 닷넷에서도 같은 의미로 사용되는데요. msdn에서도 속성 이라고 번역하고 있습니다. 우리가 일반 적으로 말하는 클래스의 속성, 즉 멤버 변수에 대한 읽기/쓰기가 가능한 메서드를 프로퍼티(Property)라고 합니다.

 

일반적으로 클래스에 변수는 private로 선언합니다.

이는 외부에서 변수를 마음대로 변경하거나 의도하지 않은 액세스를 하지 못하도록 하기 위한 캡슐화 기법입니다.

따라서 이러한 변수에 대한 접근(읽기)과 변경(쓰기)은 별도로 노출하는 공용(public) 메서드를 통해 이루어 지도록 하는데요

 

흔히 이러한 변수 엑세스 메서드를 갯터(Getter), 셋터(Setter)라고 합니다

 

닷넷에서는 변수에 대한 갯터,셋터를 따로 만들 필요 없이 프로퍼티라는 것을 통해 하나의 메서드를 통해 정의 가능토록 해 줍니다. 결국 프로퍼티는 클래스 멤버변수의 읽기/쓰기에 대한 겟터/셋터를 표현하는 간소화 하는 일종의 메서드인 셈입니다.

 

간단한 예를 보겠습니다.

 

사람을 추상화하는 Person 클래스를 정의하고 나이(age), 이름(name)이라는 멤버 변수를 가지도록 합니다.

다만 이 변수들은 외부에서 직접 접근할 수 없도록 private 로 선언하고 프로퍼티를 통해 이 변수들에 읽기/쓰기가 가능토록 구현합니다. 그리고 프로퍼티 정의를 보면 나이(age)의 경우 그 값의 범위를 제한하고 있으며 이름(name)의 경우 추가 문자열을 붙여서 반환하도록 구현했습니다. 

 

프로퍼티를 통한 이러한 제약을 가함으로써 멤버 변수에 대한 값의 무결성과 안정성을 보장하도록 하는 것입니다.

class Person{
  private int age;
  private string name;

  public int Age{
        get { return age; }

        set
        {
            if (age < 1 || age > 200)
                throw new ArgumentException("나이가 현실적이지 않습니다");
            else
                age = value;
        }
    }       

 

  public string Name
  {
      get { return "제 이름은 " + name + "입니다"; }
      set { name = value; }
  }
}

 

 

프로퍼티의 특징

그럼.. 프로퍼티의 언어적 특징과 유용성에 대해 알아 보겠습니다.

 

- 프로퍼티는 변수처럼 사용한다

프로퍼티를 정의하면 다음과 같이 사용할 수 있습니다.

 

Person person = new Person();

person.Name = "홍길동";

person.Age = 20;

Console.WriteLine(person.Name);

 

마치 클래스의 변수에 조작하는 것과 유사합니다. 즉 일반적인 메서드 호출형태가 아니라 변수 호출 형태를 띄고 있습니다. 그래서 프로퍼티를 논리적인 변수라고도 합니다.

 

- 그러나 프로퍼티는 변수가 아닙니다

프로퍼티는 변수처럼 사용할 뿐이지 실제로 변수는 아닙니다. 즉 변수와 같은 형태의 메모리 주소를 가지지는 않습니다. 따라서 주소 값을 기반으로 하는 ref, out 키워드와 함께 매개변수로 사용될 수 없습니다.

 

- 결국 프로퍼티는 메서드 입니다

프로퍼티는 실제로 메서드입니다. 프로퍼티는 클래스 멤버 변수에 대한 갯터,셋터 표현을 간단히 하기 위한 언어적 기능일 뿐이며 컴파일 시 get,set 메서드가 자동으로 만들어 집니다.

 

아래 IL코드(중간언어)를 보면 프로퍼티로 정의했던 Age Name을 위한 각각의 get, set 메서드가 추가된 것을 확인할 수 있습니다.

 

- 프로퍼티는 void 형이 될 수 없습니다

일반적인 메서드는 반환형식이 void 일 수 있지만 프로퍼티는 void 일 수 없습니다.

일반 변수가 void 형일 수 없기 때문에 변수에 대한 액세스 메서드인 프로퍼티가 void 형이 될 수 없는 것은 어찌 보면 당연한 것입니다

 

- 그 외엔 메서드와 동일합니다

void 형이 될 수 없는 것을 제외하면 일반적인 메서드의 특성을 그대로 가지고 있습니다.  virtual 선언, abstract 선언, override 재정의 등을 모두 적용할 수 있습니다

 

- Set 을 위한 value

프로퍼티의 셋터인 set 블록에서는 value 를 통해 값을 얻어 오게 됩니다. value 는 프로퍼티에서 입력 값에 해당하는 암시적 매개변수를 위한 키워드 입니다

 

public int Age

{         

  set { age = value; }

}

 

- 읽기 전용 속성

특정 멤버 변수에 대한 수정은 막고 읽기만 가능하도록 하기 위해 읽기 전용 속성을 만들 수 있습니다. 읽기 전용 속성은 프로퍼티에 set 을 정의하지 않으면 됩니다. 아래 코드는 읽기 전용 나이(age) 속성입니다

public int Age

{

    get { return age; }          

}

 

- 서로 다른 접근 제

하나의 프로퍼티에 get, set 의 접근 제한을 다르게 둘 수 있습니다. 아래 코드는 이름(name)이라는 프로퍼티에 get, set 각각 다른 접근 제한을 둔 예입니다

public string Name

{

    get { return name;  }

    protected set { name = value; }

}

 

 

 

인덱서(Indexer)

 

인덱서는 객체를 배열처럼 사용할 수 있게 하는 닷넷 언어적 기능입니다. 이 역서 프로퍼티와 같이 메서드의 특수한 표현인데요.

클래스안에 배열이나 컬렉션과 같이 복합 값이 있을 경우 유용하게 사용할 수 있습니다. 

다음 예제와 같이 클래스에 배열이 선언되어 있고 이 배열에 대한 접근을 인덱서를 통하도록 구현할 수 있습니다.

class MyClass{
    private int[] array = new int[5];

    public int this[int i]

    {
        get
        {
            return array[i];
        }

        set
        {
            array[i] = value;
        }
    }
}

 

인덱스는 this 키워드를 통해 구현합니다. 이렇게 정의된 클래스에 대한 객체는 다음과 같이 배열처럼 사용할 수 있게 됩니다

 

MyClass myClass = new MyClass();

myClass[0] = 100;

Console.WriteLine(myClass[0]);

 

 

인덱서의 특징

 

- 인덱서 역시 메서드 입니다

프로퍼티 처럼 인덱서 역시 결국 메서드입니다. IL 코드를 보면 프로퍼티와 아주 유사하게 get,set 메서드가 자동으로 추가 되어 있음을 확인할 수 있습니다

 

- 인덱서는 static 일 수 없습니다

프로퍼티는 static 일 수 있지만 인덱스는 불가능 합니다.

인덱스를 생성하는 키워드 자체가 this 입니다. 결국 클래스가 인스턴스화 되었을 때만이 사용가능 해 집니다.

 

- 인덱서도 void 형이 될 수 없습니다

- 인덱서도 변수가 아니기 때문에 ref, out 와 함께 인자로 사용될 수 없습니다

- 인덱서도 virtual , abstract, override 등이 가능합니다

 

 

인덱서의 첨자

인덱스는 객체를 배열처럼 다룰 수 있다고 했습니다.

배열은 첨자를 정수만 사용할 수 있는 반면 인덱서는 첨자가 반드시 정수 타입일 필요는 없습니다

 

다음 코드는 인덱서의 첨자로 문자열 타입(string)를 사용한 예입니다

 

class NickName{

    private Dictionary<string, string> names = new Dictionary<string, string>();

 

    public string this[string realName]

    {

        get { return names[realName]; }

        set { names[realName] = value;  }

    }

}

 

 

닷넷, 인덱스 예) String 클래스의 인덱서

닷넷이 제공하는 기본적인 라이브러리에 인덱서의 사용 예를 쉽게 찾아 볼 수 있습니다.

대표적으로 string 클래스를 들 수 있습니다.

 

string s = "mkex";

char c = s[1];// <- 'k' 가 반환된다

 

String  immutable 객체 즉 불변 객체 이기 때문에 읽기전용 인덱스만 제공하고 있습니다.

String 클래스의 인덱스 정의는 대략 아래와 같습니다.

 

class String{

 public char this[int index]{

  get {

   if(index < 0 || index >= Length)

    throw new ArgumentOutOfRangeException();

   ...

  }

 }

}

 

 

마지막으로 msdn에서 소개하는 인덱서와 프로퍼티의 차이점 표를 제시해 드리며 강좌를 마치도록 하겠습니다.

행복한 한 주 되세요~~

 

인덱서는 속성과 비슷합니다. 다음표에 나와 있는 차이점을 제외하면 접근자에 정의된 모든 규칙이 인덱서 접근자에도 적용됩니다

속성 인덱서
공용 데이터 멤버인 것처럼 메서드를 호출할 수 있습니다. 개체가 배열인 것처럼 개체에 대한 메서드를 호출할 수 있습니다.
단순한 이름을 통해 액세스할 수 있습니다. 인덱스를 통해 액세스할 수 있습니다.
정적 또는 인스턴스 멤버가 될 수 있습니다. 인스턴스 멤버여야 합니다.
속성의 get 접근자에는 매개 변수가 없습니다. 인덱서의 get 접근자는 인덱서와 동일한 형식 매개 변수 목록을 가집니다.
속성의 set 접근자에는 명시적인 value 매개 변수가 포함됩니다. 인덱서의 set 접근자는 value 매개 변수 외에도 인덱서와 동일한 형식 매개 변수 목록을 가집니다.