Collection <T> 대 List <T> 인터페이스에서 무엇을 사용해야합니까?


162

코드는 다음과 같습니다.

namespace Test
{
    public interface IMyClass
    {
        List<IMyClass> GetList();
    }

    public class MyClass : IMyClass
    {
        public List<IMyClass> GetList()
        {
            return new List<IMyClass>();
        }
    }
}

코드 분석을 실행하면 다음 권장 사항이 나타납니다.

경고 3 CA1002 : Microsoft.Design : Collection, ReadOnlyCollection 또는 KeyedCollection을 사용하도록 'IMyClass.GetList ()'에서 'List'변경

이 문제를 어떻게 해결해야하며 여기에서 좋은 방법은 무엇입니까?

답변:


234

왜 그렇지 않은지에 대한 질문의 "왜"부분에 대답하기 위해 List<T>, 그 이유는 미래 보장 및 API 단순성입니다.

미래 보장

List<T>서브 클래 싱으로 쉽게 확장 할 수 있도록 설계되지 않았습니다. 내부 구현에 적합하도록 설계되었습니다. 그 메소드가 가상이 아니므로 재정의 할 수 없으며 Add/ Insert/ Remove작업 에 대한 후크가 없다는 것을 알 수 있습니다 .

이는 나중에 컬렉션의 동작을 변경해야하는 경우 (예 : 사람들이 추가하려고하는 null 객체를 거부하거나 클래스 상태 업데이트와 같은 경우 추가 작업을 수행하려는 경우) 유형을 변경해야 함을 의미합니다. 컬렉션의 당신은 당신이 서브 클래스 할 수있는 것으로 돌아갑니다. 인터 클래스 변경이 될 것입니다 (물론 null을 허용하지 않는 것과 같은 의미를 변경하는 것도 인터페이스 변경 일 수도 있지만 내부 클래스 상태를 업데이트하는 것은 아닙니다).

그러니 쉽게 등의 서브 클래스 수있는 클래스 반환하여 Collection<T>같은 인터페이스 또는 IList<T>, ICollection<T>또는 IEnumerable<T>이 여전히 반환 할 수 있기 때문에 소비자의 코드를 파괴하지 않고, 당신이 당신의 요구를 충족하기 위해 다른 콜렉션 유형을로 내부 구현을 변경할 수 있습니다 그들이 기대하는 유형.

API 단순성

List<T>같은 유용한 작업을 많이 포함하고 BinarySearch, Sort등등을. 그러나 이것이 콜렉션 인 경우 소비자가 아닌 목록의 의미를 제어 할 가능성이 있습니다. 따라서 클래스 내부에서 이러한 작업이 필요할 수 있지만 클래스의 소비자가 호출하기를 원치 않을 수도 있습니다.

따라서 더 간단한 콜렉션 클래스 또는 인터페이스를 제공하여 API 사용자가 보는 멤버 수를 줄이고 사용하기가 더 쉽습니다.


1
이 응답은 끝났습니다. 주제에 대한 또 다른 좋은 읽을
거리는

7
첫 번째 요점을 볼 수 있지만 API 단순성 부분에 동의하는지 모르겠습니다.
보리스 칼 렌스

stackoverflow.com/a/398988/2632991 Collections와 Lists의 차이점에 대한 실제 게시물도 있습니다.
El Mac

2
blogs.msdn.com/fxcop/archive/2006/04/27/…- > 죽었습니다. 이에 대한 최신 정보가 있습니까?
Machado


50

구체적인 컬렉션이 아닌 인터페이스를 반환하도록 개인적으로 선언합니다. 정말로 목록 액세스를 원하면을 사용하십시오 IList<T>. 그렇지 않으면, 생각 ICollection<T>하고 IEnumerable<T>.


1
그러면 IList가 ICollection 인터페이스를 확장합니까?
bovium

8
@ 존 : 나는 이것이 오래되었다는 것을 알고 있지만 blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx 에서 Krzysztof의 말에 대해 언급 할 수 있습니까? 특히 그의 의견 We recommend using Collection<T>, ReadOnlyCollection<T>, or KeyedCollection<TKey,TItem> for outputs and properties and interfaces IEnumerable<T>, ICollection<T>, IList<T> for inputs. CA1002는 Krzysztof의 의견과 일치하는 것으로 보입니다. 인터페이스 대신에 구체적인 컬렉션이 권장되는 이유와 입력 / 출력의 차이점을 상상할 수 없습니다.
Nelson Rothermel

3
@Nelson : 호출자가 불변리스트를 전달 하도록 요구 하는 경우는 드물지만 , 불변리스트를 반환 하도록하는 것이 합리적 입니다. 그래도 다른 컬렉션에 대해서는 확실하지 않습니다. 더 자세하게 설명해 드리겠습니다.
Jon Skeet

2
특정한 경우가 아닙니다. 분명히 일반적으로 ReadOnlyCollection<T>입력에는 의미가 없습니다. 마찬가지로 IList<T>입력에 따르면 출력에 의미가없는 "Sort () 또는 IList에있는 다른 멤버가 필요합니다"라고 말합니다. 그러나 내가 의미 ICollection<T>하는 바는 입력 및 Collection<T>출력 으로 권장되는 이유입니다 . 왜 하지 사용 ICollection<T>이 제안 또한 출력으로?
Nelson Rothermel

9
나는 그것이 모호성과 관련이 있다고 생각합니다. Collection<T>그리고 ReadOnlyCollection<T>둘 다에서 파생됩니다 ICollection<T>(즉, 없습니다 IReadOnlyCollection<T>). 인터페이스를 반환하면 어떤 인터페이스인지, 수정할 수 있는지 확실하지 않습니다. 어쨌든, 귀하의 의견에 감사드립니다. 이것은 좋은 위생 검사였습니다.
Nelson Rothermel

3

아직 아무도 왜 "왜"에 대답하지 않았다고 생각합니다. a Collection<T>대신 "왜"를 사용해야하는 List<T>이유는를 노출하면 List<T>객체에 액세스 할 수있는 모든 사람이 목록의 항목을 수정할 수 있기 때문 입니다. 반면 Collection<T>에 "Add (추가)", "Remove (제거)"등의 방법을 만들고 있음을 나타냅니다.

당신은 아마 당신 자신을 위해 (또는 아마도 몇 명의 동료를 위해) 인터페이스를 코딩하고 있기 때문에 걱정할 필요가 없습니다. 이해가 될만한 또 다른 예가 있습니다.

공개 배열이있는 경우 예 :

public int[] MyIntegers { get; }

아무도 값을 엉망으로 만들 수없는 "get"접근자가 있기 때문에 사실이 아니라고 생각할 것입니다. 누구나 다음과 같이 내부의 값을 변경할 수 있습니다.

someObject.MyIngegers[3] = 12345;

개인적 List<T>으로 대부분의 경우에 사용합니다. 그러나 임의의 개발자에게 제공 할 클래스 라이브러리를 디자인하고 객체의 상태에 의존 해야하는 경우 자신의 컬렉션을 만들고 거기에서 잠그고 싶을 것입니다. )


"클라이언트 코드에 List <T>를 반환하면 클라이언트 코드가 컬렉션을 수정할 때 알림을받을 수 없습니다." - FX의 COP는 ... 또한 "을 참조하십시오 msdn.microsoft.com/en-us/library/0fss9skc.aspx 와우, 나는 모든 : 후 기본 떨어져 아니에요 것 같다 ..."
디모데 Khouri을

와우-몇 년 후 위의 주석에 붙여 넣은 링크의 끝에 인용 부호가 있었으므로 작동하지 않았습니다 ... msdn.microsoft.com/en-us/library/0fss9skc.aspx
Timothy Khouri

2

List 객체를 직접 조작하는 대신 노출을 직접 추상화하는 것입니다.

다른 객체 (또는 사람)가 객체의 상태를 직접 수정하도록하는 것은 좋지 않습니다. 부동산 게터 / 세터를 생각하십시오.

컬렉션-> 일반 컬렉션의 경우
ReadOnlyCollection-> 수정하지 않아야하는 컬렉션의 경우
KeyedCollection-> 사전을 대신 사용하려는 경우.

이 문제를 해결하는 방법은 클래스에서 수행하려는 작업과 GetList () 메서드의 목적에 따라 다릅니다. 정교하게 할 수 있습니까?


1
그러나 Collection <T> 및 KeyedCollection <,>도 읽기 전용이 아닙니다.
nawfal

@nawfal 그리고 나는 그것이 어디에 있다고 말했습니까?
chakrit

1
당신은 상태가 다른 개체 (또는 사람)의 오브젝트에 직접의 상태를 수정할 수 없습니다 및 목록 3 수집 유형. ReadOnlyCollection다른 두 사람을 금지 하는 것은 그것을 따르지 않습니다.
nawfal

그것은 "좋은 습관"을 의미합니다. 적절한 맥락으로 부탁드립니다. OP가 경고를 이해하기를 원하기 때문에 아래의 목록에는 이러한 유형의 기본 요구 사항이 나와 있습니다. 그런 다음 계속 GetList()해서 더 정확하게 도울 수 있는 목적에 대해 물어 보았습니다.
chakrit

1
좋아, 나는 그 부분을 이해한다. 그러나 여전히 추론 부분은 나에 따르면 소리가 아닙니다. 당신은 말할 Collection<T>내부 구현을 추상화하는 데 도움이 내부 목록을 직접 조작을 방지 할 수 있습니다. 어떻게? Collection<T>전달 된 동일한 인스턴스에서 작동하는 랩퍼 일뿐입니다. 상속을위한 동적 컬렉션이지만 다른 것은 없습니다 (Greg의 답변이 더 관련이 있습니다).
nawfal

1

이런 종류의 경우에는 보통 필요한 최소한의 구현을 노출하려고합니다. 소비자가 실제로 목록을 사용하고 있음을 알 필요가 없으면 목록을 반환 할 필요가 없습니다. Microsoft가 컬렉션을 제안함에 따라 반환하면 클래스 소비자로부터 목록을 사용하고 있다는 사실을 숨기고 내부 변경으로부터 격리시킵니다.


1

이 요청 이후 오랜 시간이 지났지 만 추가해야 할 것이 있습니다.

목록 유형이에서 List<T>대신 파생되는 Collection<T>경우 구현되는 보호 된 가상 메소드를 Collection<T>구현할 수 없습니다 . 이것은 목록을 수정 한 경우 파생 된 유형이 응답 할 수 없음을 의미합니다. List<T>항목을 추가하거나 제거 할 때 사용자가 알고 있다고 가정 하기 때문 입니다. 알림에 응답 할 수있는 것은 오버 헤드이므로 List<T>제공하지 않습니다.

외부 코드가 컬렉션에 액세스 할 수있는 경우 항목을 추가하거나 제거 할시기를 제어하지 못할 수 있습니다. 따라서 Collection<T>목록이 수정 된시기를 알 수있는 방법을 제공합니다.


0

다음과 같은 것을 반환하는 데 아무런 문제가 없습니다.

this.InternalData.Filter(crteria).ToList();

연결이 끊긴 내부 데이터 복사본 을 반환 하거나 데이터 쿼리 결과를 분리 한 경우 List<TItem>구현 세부 정보를 노출하지 않고 안전하게 반환 할 수 있으며 반환 된 데이터를 편리한 방식으로 사용할 수 있습니다.

그러나 이것은 내가 기대하는 소비자 유형에 달려 있습니다. 이것이 데이터 그리드와 같은 경우 대부분의 경우 IEnumerable<TItem> 복사 된 항목 목록이 될 것입니다.


-1

Collection 클래스는 실제로 다른 컬렉션 주위의 래퍼 클래스이므로 구현 세부 정보 및 기타 기능을 숨길 수 있습니다. 나는 이것이 객체 지향 언어의 코딩 패턴 숨기기 속성과 관련이 있다고 생각합니다.

걱정하지 않아도된다고 생각하지만 실제로 코드 분석 도구를 사용하려면 다음을 수행하십시오.

//using System.Collections.ObjectModel;

Collection<MyClass> myCollection = new Collection<MyClass>(myList);

1
죄송합니다. 오타였습니다. Collection <MyClass>를 언급합니다. 나는 C # 4와 일반 공분산 btw에 손을 대길 정말로 기대합니다!
Tamas Czinege

Collection<T>내가 보호 하는 한 포장지 자체는 그 자체도 물론 기본 수집 물도 보호하지 않습니다.
nawfal

그 코드는 "코드 분석 도구를 만족 시키십시오". @TamasCzinege가 사용 Collection<T>하면 즉시 기본 컬렉션을 보호 한다고 말하지 않습니다 .
chakrit
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.