불변 클래스는 어떻게 만듭니 까?


113

불변 클래스를 만드는 중입니다.
모든 속성을 읽기 전용으로 표시했습니다.

수업에 항목 목록이 있습니다.
속성이 읽기 전용 인 경우 목록을 수정할 수 있습니다.

목록의 IEnumerable을 노출하면 변경할 수 없습니다.
클래스를 불변으로 만들기 위해 따라야하는 기본 규칙이 무엇인지 알고 싶었습니다.


2
Immutability에 대한 Eric Lippert의 블로그 시리즈 , 특히 "kinds of immutability" 항목을 읽을 것을 강력히 권장합니다 . "목록의 IEnumerable을 노출하면 불변으로 만든다"는 귀하의 의견은 나에게 다소 이상하게 보입니다. 그게 무슨 뜻이야?
— Jon Skeet

내가 의미하는 바는 목록 (객체에 다른 객체의 목록이있는 경우)에 대한 액세스를 허용하는 것이 아니라 사용자가 IEnumerable을 사용하여 구성원에 액세스 할 수 있도록 허용하는 것입니다. 특정 예에서와 같이 여기에 말하는 목록이지만 모든 데이터 구조가 될 수 있습니다.
— Biswanath

1
Eric Lippert의 블로그 시리즈에 대한 Jon Skeet의 링크는 MSDN이 이러한 블로그를 보관할 때 깨졌습니다. "불변의 종류"를 참조하십시오 . 나머지 시리즈는 왼쪽 패널에서 2007 년 12 월 + 2008 년 1 월 미만인 것으로 보입니다.
— John Doe

사람들이 일반적으로 용어 혼동 방법에 에릭 Lippert의 블로그 시리즈를 참조하십시오 atomicity, volatility그리고 immutability: 부품 하나 , 2 부 및 3 부 . 이것들은 그의 개인 블로그에서 왔으며 그의 MSDN 게시물보다 초보자에게 더 친숙하다고 생각합니다.
— John Doe

답변:


121

나는 당신이 올바른 길을 가고 있다고 생각합니다.

  • 클래스에 주입 된 모든 정보는 생성자에 제공되어야합니다.
  • 모든 속성은 getter 여야합니다.
  • 컬렉션 (또는 배열)이 생성자에 전달되면 호출자가 나중에 수정하지 못하도록 복사해야합니다.
  • 컬렉션을 반환하려면 복사본 또는 읽기 전용 버전을 반환합니다 (예 : ArrayList.ReadOnly 또는 이와 유사한 사용)-이것을 이전 지점과 결합하고 다음 경우에 반환 될 읽기 전용 복사본을 저장할 수 있습니다. 호출자가 액세스), 열거자를 반환하거나 컬렉션에 대한 읽기 전용 액세스를 허용하는 다른 메서드 / 속성을 사용합니다.
  • 멤버 중 하나라도 변경 가능한 경우 여전히 변경 가능한 클래스의 모양을 가질 수 있습니다.이 경우 유지하려는 상태를 복사하고 전체 변경 가능한 객체를 복사하지 않는 한 반환하지 않아야합니다. 호출자에게 돌려주기 전에-또 다른 옵션은 변경 가능한 객체의 변경 불가능한 "섹션"만 반환하는 것입니다. @Brian Rasmussen 덕분에이 점을 확장 할 수 있습니다.

조회 만 할 수있는 래퍼 조회 속성을 작성해야합니까? 그리고 대답 해 주셔서 감사합니다.
— Biswanath

5
생성자에 인수로 전달 된 변경 가능한 참조 유형은 복사해야합니다. 그렇지 않으면 발신자는 여전히 상태에 대한 참조를 보유합니다.
— Brian Rasmussen

@Brian Rasmussen, 맞습니다.하지만 복사 된 경우에도 제공된 acces.sors에 따라 모든 호출자가 변경 가능한 개체에 액세스 할 수 있습니다. 변경 가능한 객체가 전달되는 경우 클래스는 항상 객체의 다른 복사본 또는 변경 불가능한 섹션을 반환하는 것이 가장 좋습니다
— Blair Conrad

@Blair-클래스에 사전이있는 경우 검색에 사용하고 싶습니다. 조회를 수행하는 읽기 전용 속성입니까?
— Biswanath

@Biswanath-예, 사전의 값을 변경할 수있는 경우 반환하기 전에 보호해야한다는 경고와 함께 변경하지 않으려면
— Blair Conrad

18

변경 불가능하게하려면 모든 속성과 필드가 읽기 전용이어야합니다. 그리고 모든 목록의 항목은 그 자체로 불변이어야합니다.

다음과 같이 읽기 전용 목록 속성을 만들 수 있습니다.

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}

1
ImmutableList가 더 좋을 것이라고 생각합니다.
— Kevin Wong

사용 ImmutableList는 또한 클래스 밀봉 고려
— kofifus

ImmutableList가이 사용 사례에 적합하다고 확신하지 않습니다 basic rules to make a class (that contains a list) immutable. ImmutableList 시맨틱은 목록의 사본에 항목을 추가 할 때보다 효율적인 메모리 사용을 가능하게하지만, 나에게는 더 구체적인 사용 사례 인 것 같습니다.
— Joe

11

또한 다음 사항에 유의하십시오.

public readonly object[] MyObjects;

readonly 키워드로 표시된 경우에도 변경 불가능하지 않습니다. 인덱스 접근 자별로 개별 배열 참조 / 값을 변경할 수 있습니다.


5

ReadOnlyCollection수업을 사용하십시오 . 그것은에 위치하고있어 System.Collections.ObjectModel공간.

목록 (또는 생성자)을 반환하는 모든 항목에서 목록을 읽기 전용 컬렉션으로 설정합니다.

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}

1

또 다른 옵션은 내부 컬렉션을 전혀 노출하는 대신 방문자 패턴을 사용하는 것입니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.