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


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.