읽기 전용 개체를 반환하는 모범 사례


11

C #의 OOP에 대한 "모범 사례"질문이 있지만 모든 언어에 적용됩니다.

속성 접근자를 통해 공개 될 객체가있는 라이브러리 클래스를 갖는 것을 고려하지만, 우리는 일반인 (이 라이브러리 클래스를 사용하는 사람들)이 라이브러리 클래스를 변경하는 것을 원하지 않습니다.

class A
{
    // Note: List is just example, I am interested in objects in general.
    private List<string> _items = new List<string>() { "hello" }

    public List<string> Items
    {
        get
        {
            // Option A (not read-only), can be modified from outside:
            return _items;

            // Option B (sort-of read-only):
            return new List<string>( _items );

            // Option C, must change return type to ReadOnlyCollection<string>
            return new ReadOnlyCollection<string>( _items );
        }
    }
}

분명히 가장 좋은 방법은 "옵션 C"이지만 ReadOnly 변형이있는 객체는 거의 없습니다 (그리고 사용자 정의 클래스도 없습니다).

당신이 클래스 A의 사용자라면,

List<string> someList = ( new A() ).Items;

원본 (A) 객체로 전파 하시겠습니까? 아니면 주석 / 문서로 작성된 복제본을 반환해도 괜찮습니까? 이 클론 접근 방식은 추적하기 어려운 버그로 이어질 수 있다고 생각합니다.

C ++에서는 const 객체를 반환 할 수 있으며 const로 표시된 메소드 만 호출 할 수 있다는 것을 기억합니다. C #에 그러한 기능 / 패턴이 없다고 생각합니까? 그렇지 않은 경우 왜 포함하지 않습니까? 나는 그것이 const 정확성이라고 믿습니다.

그러나 다시 나의 주요 질문은 "무엇을 기대 하느냐"또는 옵션 A와 B를 처리하는 방법에 관한 것입니다.


2
.NET 4.5의 새로운 불변 ​​콜렉션의 미리보기에 대한이 기사를보십시오. blogs.msdn.com/b/bclteam/archive/2012/12/18/… NuGet을 통해 이미 얻을 수 있습니다.
Kristof Claes

답변:


10

컬렉션에 가장 적합한 솔루션 인 @Jesse의 답변 외에도 일반적인 아이디어는 A의 공용 인터페이스를 반환하는 방식으로 디자인하는 것입니다.

  • 값 유형 (예 : int, double, struct)

  • 변경 불가능한 객체 (예 : "문자열"또는 클래스 A와 마찬가지로 읽기 전용 메서드 만 제공하는 사용자 정의 클래스)

  • (필요한 경우에만) : "옵션 B"와 같은 개체 사본이 보여줍니다.

IEnumerable<immutable_type_here> 그 자체로는 불변이므로, 이것은 위의 특별한 경우입니다.


19

또한 인터페이스에 프로그래밍해야하며 그 속성에서 반환하려는 항목은 IEnumerable<string>입니다. A List<string>는 일반적으로 변경 가능하기 때문에 조금은 아닙니다. Liskov 대체 원칙을 위반하는 것처럼 느끼는 ReadOnlyCollection<string>구현 자로 말 ICollection<string>하겠습니다.


6
+1, IEnumerable<string>여기에 원하는 것은 an을 사용하는 것 입니다.
Doc Brown

2
.Net 4.5에서는을 사용할 수도 있습니다 IReadOnlyList<T>.
svick

3
다른 이해 관계자를위한 참고 사항. 속성 접근자를 고려할 때 : return _list (+ implicit cast to IEnumerable)를 수행하여 List <string> 대신 IEnumerable <string>을 반환하면이 목록을 List <string>으로 다시 캐스팅하고 변경할 수 있습니다. 주 객체로 전파됩니다. 내부 목록을 보호하는 새 List <string> (_list) (+ 다음으로 내재 된 캐스트를 IEnumerable로)를 반환 할 수 있습니다. 자세한 내용은 여기를 참조하십시오 : stackoverflow.com/questions/4056013/…
NeverStopLearning

1
인덱스로 액세스해야하는 컬렉션의받는 사람이 데이터를 쉽게 처리 할 수있는 형식으로 데이터를 복사 할 수 있도록 준비해야하거나 컬렉션을 반환하는 코드가 다음과 같은 형식으로 데이터를 제공하도록하는 것이 좋습니다. 색인으로 액세스 할 수 있습니까? 공급 업체가 인덱스로 액세스를 용이하게하지 않는 형태로 제공하지 않는 한, 수신자가 자신의 중복 사본을 만들 필요가 없도록하는 가치가 공급 업체가 공급할 필요가없는 가치를 초과한다고 생각합니다 랜덤 액세스 형식 (예 : 읽기 전용 IList<T>)
supercat

2
IEnumerable에 대해 내가 싫어하는 한 가지는 그것이 코 루틴으로 실행되는지 또는 단지 데이터 객체인지 여부를 알 수 없다는 것입니다. 그것이 코 루틴으로 실행되면 미묘한 버그가 생길 수 있으므로 때때로 아는 것이 좋습니다. 이것은 내가 등 ReadOnlyCollection 또는 IReadOnlyList 선호하는 경향이 이유
스티브 Vermeulen에게

3

비슷한 문제가 발생했습니다. 아래와 같은 문제가 내 솔루션이지만 라이브러리를 제어하는 ​​경우에만 작동합니다.


수업 시작 A

public class A
{
    private int _n;
    public int N
    {
        get { return _n; }
        set { _n = value; }
    }
}

그런 다음 속성의 일부와 읽기 방법 만 사용하여 인터페이스를 파생시키고 A 구현

public interface IReadOnlyA
{
    public int N { get; }
}
public class A : IReadOnlyA
{
    private int _n;
    public int N
    {
        get { return _n; }
        set { _n = value; }
    }
}

물론 읽기 전용 버전을 사용하려면 간단한 캐스트로 충분합니다. 이것이 바로 C 솔루션의 해결책이지만, 달성 한 방법을 제안 할 것이라고 생각했습니다.


2
이것의 문제는 그것을 가변 버전으로 다시 캐스팅 할 수 있다는 것입니다. 그리고 기본 클래스 (이 경우 인터페이스) 메소드를 통해 관찰 할 수있는 LSP의 위반은 서브 클래스의 새로운 메소드에 의해 변경 될 수 있습니다. 그러나 강제하지 않더라도 최소한 의도를 전달합니다.
user470365

1

이 질문을 찾고 여전히 답변에 관심이있는 사람이라면 누구나 다른 옵션이 노출 ImmutableArray<T>됩니다. 다음은 내가 더 나은 다음 생각하는 이유 몇 가지 포인트입니다 IEnumerable<T>또는 ReadOnlyCollection<T>:

  • 불변이라고 명시하고있다 (표현 API)
  • 컬렉션이 이미 형성되어 있음을 분명히 나타냅니다 (즉, 게으르게 초기화되지 않았으며 여러 번 반복해도 괜찮습니다)
  • 아이템의 개수를 알고 있다면, 아는 사람과 같은 것을 숨기지 IEnumerable<T>않습니다.
  • 클라이언트는 구현이 무엇인지 알지 못 IEnumerable하거나 IReadOnlyCollect결코 변경되지 않는다고 가정 할 수 없기 때문에 (즉, 컬렉션에 대한 참조가 변경되지 않은 채 컬렉션의 항목이 변경되지 않았다는 보장은 없습니다)

또한 APi가 컬렉션의 변경 사항을 클라이언트에게 알려야하는 경우 이벤트를 별도의 멤버로 노출합니다.

나는 그것이 IEnumerable<T>일반적으로 나쁘다는 것을 말하는 것이 아닙니다 . 컬렉션을 인수로 전달하거나 느리게 초기화 된 컬렉션을 반환 할 때 여전히 사용합니다 (이 경우 아직 알려지지 않았기 때문에 항목 수를 숨기는 것이 좋습니다).

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