읽기 전용으로 착각하기 쉬운 컬렉션 속성(Property)에 대하여

[제목] 읽기 전용으로 착각하기 쉬운 컬렉션 속성(Property)에 대하여

C# 개발자들이 간혹 실수하는 것 중에 하나는 컬렉션을 리턴하는 속성(Property)에 대한 것이다. 만약 컬렉션을 리턴하는 속성이 Getter 즉 get 만 가지고 있을 때, 이 속성은 진정한 읽기 전용(Read Only)이라고 볼 수 있을까?

예를 들어 다음 C# 코드를 살펴보자.

class Class1
{
    private List<int> _keys = new List<int>();
    public List<int> Keys
    {
       get { return _keys; }
    }
}

여기서 Keys 라는 속성은 set이 없고 get만을 가지고 있다. 보통의 경우 get 만을 가진 속성은 읽기 전용이라 볼 수 있다. 하지만 리턴하는 대상이 컬렉션인 경우, 이는 컬렉션 자체에 대한 레퍼런스 포인터만 읽기 전용이라는 것을 의미하고, 리턴된 컬렉션의 요소에 대해서는 외부에서 변경 가능하다는 것을 의미한다.

즉, 다음과 같이 Keys 컬렉션에 요소를 추가 혹은 삭제하는 일이 가능해 진다. 따라서 컬렉션 Getter는 컬렉션 레퍼런스 자체는 변경할 수 없지만, 컬렉션 안의 요소는 마음대로 변경할 수 있는 것을 의미한다.

Class1 c = new Class1();
List<int> list = c.Keys;

list.Add(10);
list.Remove(1);

그렇다면 컬렉션 속성에 set을 사용한다는 것은 무슨 의미일까? 이는 클래스 내부에 사용하던 _keys 컬렉션 자체를 버리고 다른 새로운 컬렉션으로 전체를 대체해 버린다는 것을 의미한다.

마지막으로 그러면, 컬렉션 요소들이 변경되지 않도록 컬렉션 전체를 Read-Only로 하기 위해서는 어떻게 해야 할까? 만약 외부에서 Keys 속성을 foreach 등을 사용해 열거식으로만 사용한다면, Keys 속성의 리턴 타입을 읽기 전용인 IEnumerable로 변경하면 된다. IEnumerable 인터페이스는 List와 다르게 Add() 혹은 Remove() 같은 메서드를 지원하지 않기 때문에서 호출자(Caller)가 컬렉션 요소를 변형할 수 없게 한다.

public IEnumerable<int> Keys
{
    get { return _keys; }
}

만약 외부에서 Keys 컬렉션을 C# Indexer로 사용해야 한다면, IEnumerable은 인덱서를 지원하지 않기 때문에 IEnumerable을 사용할 수 없게 된다. List 클래스의 인터페이스 계층 IList, ICollection, IEnumerable 중 인덱서를 지원하는 것은 IList인데, 이 IList는 Add/Remove를 가지고 있기 때문에 컬렉션을 Read-only로 만들 수 없다. 만약 List 데이타량이 작다면 Clone해서 리턴할 수도 있을텐데, 이는 외부에서는 추가 삭제 기능을 사용할 수 있지만, 이 변경사항이 Class1 객체에는 전파되지 않는 혼돈을 가져올 수 있다.

이러한 문제점을 해결하기 위해 .NET에 ReadOnlyCollection 이라는 클래스를 사용할 수 있는데, 이는 IList<T> 를 구현하는 객체를 읽기 전용으로 변경하는 Wrapper 클래스이다. 이 클래스를 사용하여 위의 Keys 속성을 변경하면 다음과 같이 될 것이다.

public ReadOnlyCollection<int> Keys
{
    get 
    {
        var ro = new ReadOnlyCollection<int>(_keys);
        return ro;
    }
}

Laters...



본 웹사이트는 광고를 포함하고 있습니다. 광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.