IList 사용시기 및 List 사용시기


180

나는 IList가 인터페이스이고 List가 구체적인 유형이라는 것을 알고 있지만 여전히 각 유형을 언제 사용 해야할지 모르겠습니다. 내가 지금하고있는 일은 인터페이스를 사용하는 Sort 또는 FindAll 메서드가 필요하지 않은 것입니다. 내가 맞아? 인터페이스 또는 콘크리트 유형을 언제 사용할지 결정하는 더 좋은 방법이 있습니까?


1
여전히 궁금한 사람이 있으면 여기에서 가장 좋은 답변을 찾으십시오. stackoverflow.com/questions/400135/listt-or-ilistt
Crismogram

답변:


175

내가 따르는 두 가지 규칙이 있습니다.

  • 작동하는 가장 기본적인 유형을 수락하십시오
  • 사용자에게 필요한 가장 풍부한 유형을 반환

따라서 컬렉션을 취하는 함수 나 메서드를 작성할 때는 List가 아니라 IList <T>, ICollection <T> 또는 IEnumerable <T>를 사용하도록 작성하십시오. System.Object도 T 일 수 있기 때문에 이종 목록에도 일반 인터페이스가 작동합니다. 이렇게하면 스택이나 다른 데이터 구조를 사용하기로 결정한 경우 골치 아프게됩니다. 함수에서해야 할 모든 것이 foreach이면 IEnumerable <T> 만 있으면됩니다.

반면, 함수에서 객체를 반환 할 때는 사용자가 캐스트하지 않고도 가장 풍부한 작업 집합을 사용자에게 제공하려고합니다. 이 경우 내부적으로 List <T>이면 복사본을 List <T>로 반환하십시오.


43
입력 / 출력 유형을 다르게 취급해서는 안됩니다. 입력 및 출력 유형은 모두 클라이언트 요구를 지원할 가장 기본적인 유형 (바람직하게는 인터페이스)이어야합니다. 캡슐화는 클라이언트에게 가능한 한 클래스 구현에 대해 알리는 것에 의존합니다. 구체적인 목록을 반환하면 모든 클라이언트가 다시 컴파일 / 업데이트하지 않고 다른 더 나은 유형으로 변경할 수 없습니다.
애쉬

11
나는 두 규칙에 동의하지 않습니다 ... 나는이 경우 IList (더 나은 IEnumarable)를 반환 할 때 가장 원시적 인 유형과 전문성을 사용하고 내부 함수에서 List로 작업해야합니다. 그런 다음 "add"또는 "sort"가 필요할 때 Collection을 사용하고 더 필요한 경우 List를 사용하십시오. 그래서 나의 어려운 규칙은 : 항상 IENumarable로 시작하고 더 많은 것을 원한다면 다음과 같이 확장하십시오.
ethem

2
편의상 "두 가지 규칙"의 이름은 견고성 원칙 (포스텔 법칙) 입니다.
easoncxz 2016 년

가장 기본적인 유형을 반환할지 가장 풍부한 유형을 반환할지에 대한 토론의 어느 쪽이든, 고려해야 할 것은 매우 간단한 인터페이스를 반환 할 때 소비 코드는 종종 (항상은 아니지만) 키워드 if...else와 체인을 사용하여 is계산할 수 있다는 것입니다 훨씬 더 풍부한 유형을 사용하여 캐스팅하고 어쨌든 사용합니다. 따라서 기본 인터페이스를 사용하여 가리지 않고 숨기는 것이 반드시 보장되는 것은 아닙니다 . 그러나 어렵게 만들면 소비 코드 작성자가 코드 사용 방식에 대해 두 번 생각하게 할 수 있습니다.
Panzercrisis

6
특히 서비스 / api 경계에있는 경우 포인트 # 2에 대해 매우 동의하지 않습니다. 수정 가능한 컬렉션을 반환하면 컬렉션이 "라이브"하고 호출 메서드 가 컬렉션 Add()과 유사 하고 Remove()컬렉션 이외의 효과를 가질 수 있다는 인상을 줄 수 있습니다. IEnumerable데이터 검색 방법을 사용하는 경우 와 같이 읽기 전용 인터페이스를 반환하는 경우가 많습니다. 소비자는 필요에 따라 더 풍부한 유형으로 투사 할 수 있습니다.
STW

56

FxCop에서 확인한 Microsoft 지침은 공용 API에서 List <T> 사용을 권장하지 않습니다. IList <T>를 선호하십시오.

또한 이제는 거의 항상 1 차원 배열을 IList <T>로 선언하므로 Array.Length 대신 IList <T> .Count 속성을 일관되게 사용할 수 있습니다. 예를 들면 다음과 같습니다.

public interface IMyApi
{
    IList<int> GetReadOnlyValues();
}

public class MyApiImplementation : IMyApi
{
    public IList<int> GetReadOnlyValues()
    {
        List<int> myList = new List<int>();
        ... populate list
        return myList.AsReadOnly();
    }
}
public class MyMockApiImplementationForUnitTests : IMyApi
{
    public IList<int> GetReadOnlyValues()
    {
        IList<int> testValues = new int[] { 1, 2, 3 };
        return testValues;
    }
}

3
나는이 설명 / 예를 가장 좋아합니다!
JonH

28

사람들이 항상 간과하는 중요한 것이 있습니다.

일반 배열을 IList<T>매개 변수 를 허용하는 것으로 전달한 다음 호출 IList.Add()하여 런타임 예외를 수신 할 수 있습니다 .

Unhandled Exception: System.NotSupportedException: Collection was of a fixed size.

예를 들어 다음 코드를 고려하십시오.

private void test(IList<int> list)
{
    list.Add(1);
}

다음과 같이 호출하면 런타임 예외가 발생합니다.

int[] array = new int[0];
test(array);

이는 일반 배열을 사용 IList<T>하여 Liskov 대체 원칙 을 위반 하기 때문에 발생합니다 .

이러한 이유로 전화 IList<T>.Add()를 거는 경우 List<T>대신을 (를) 요구하는 것이 좋습니다 IList<T>.


이것은 모든 인터페이스에 대해 사소한 사실입니다. 당신이 당신의 주장을 따르기를 원한다면, 어떤 인터페이스를 전혀 사용하지 말라고 주장 할 수있는 것보다 그것의 구현이 던져 질 수 있기 때문입니다. 반면에 OP가 제시 한 제안 List<T>보다 선호하는 제안을 고려할 경우 권장 IList<T>이유를 알고 있어야 IList<T>합니다. (예를 들어 blogs.msdn.microsoft.com/kcwalina/2005/09/26/... )
미샤 Wiedenmann

3
@MichaWiedenmann 여기에 내 대답은 당신이 전화를 할 때에 만 해당됩니다 IList<T>.Add(). 나는 당신 사용 해서는 안된다는 말이 아닙니다.IList<T> 단지 가능한 함정을 지적하고 있습니다. (내가 사용하는 경향이 IEnumerable<T>또는 IReadOnlyList<T>IReadOnlyCollection<T>에 우선 IList<T>내가 할 수있는 경우.)
매튜 왓슨

24

나는 매개 변수를 취했지만 반환하지 않는 Lee의 조언에 동의합니다.

인터페이스를 반환하도록 메서드를 지정하면 소비하는 메서드를 모르더라도 나중에 정확한 구현을 자유롭게 변경할 수 있습니다. 나는 List <T>에서 변경할 필요가 없다고 생각했지만 나중에 추가 기능을 위해 사용자 지정 목록 라이브러리를 사용하도록 변경해야했습니다. IList <T> 만 반환했기 때문에 라이브러리를 사용하는 사람들은 코드를 변경하지 않아도됩니다.

물론 외부에서 볼 수있는 메소드 (공용 메소드)에만 적용하면됩니다. 나는 내부 코드에서도 개인적으로 인터페이스를 사용하지만 변경 사항을 변경하면 모든 코드를 직접 변경할 수 있으므로 반드시 필요한 것은 아닙니다.


22

IEnumerable
목적에 가장 적합한 유형을 사용하지 않아야합니다.
IEnumerable보다 구체적이지 않습니다 IList. 컬렉션의 항목을 반복 할 때
사용 IEnumerable합니다.

IList가
IList 구현 IEnumerable합니다. 컬렉션에 인덱스로 액세스하고 요소를 추가 및 삭제하는 등의 작업이 필요할 때
사용해야합니다 IList.


List구현 목록IList .


3
훌륭하고 명확한 답변으로 도움이되었습니다. 그러나 대부분의 개발자들에게 프로그램 크기와 성능의 작은 차이는 걱정할 필요가 없습니다.
Graham Laight

9

항상 가장 낮은 기본 유형을 사용하는 것이 가장 좋습니다. 이를 통해 인터페이스의 구현 자 또는 메소드의 소비자에게이면에서 원하는 것을 사용할 수있는 기회가 제공됩니다.

컬렉션의 경우 가능한 경우 IEnumerable을 사용하는 것이 좋습니다. 이것은 가장 융통성이 있지만 항상 적합하지는 않습니다.


1
그것은 항상 최선 수용 가능한 최저 기본 유형을. 귀국은 다른 이야기입니다. 유용한 옵션을 선택하십시오. 클라이언트가 인덱스 액세스를 사용하고 싶을 것이라고 생각하십니까? 이미 목록 인 ToList()반품 은 받지 말고 대신 IEnumerable<T>반품하십시오 IList<T>. 이제 고객은 노력없이 제공 할 수있는 혜택을 누릴 수 있습니다.
Timo

5

단일 메소드 내에서 (또는 경우에 따라 단일 클래스 또는 어셈블리에서) 작업 중이고 외부에서 아무도 수행중인 작업을 보지 못하는 경우에는 전체 목록을 사용하십시오. 그러나 메소드에서 목록을 반환 할 때와 같이 외부 코드와 상호 작용하는 경우 특히 특정 구현에 자신을 연결하지 않고 인터페이스를 선언하려고합니다. 특히 나중에 코드. 구체적인 유형으로 시작하고 동일한 인터페이스를 사용하더라도 다른 유형으로 변경하기로 결정한 경우 인터페이스 또는 추상 기본 유형으로 시작하지 않으면 다른 사람의 코드가 손상됩니다.


4

나는 이런 종류의 일에 대해 단단하고 빠른 규칙이 없다고 생각하지만, 절대적으로 필요할 때까지 가능한 가장 가벼운 방법을 사용하는 지침을 따릅니다.

예를 들어, Person수업과 수업 이 있다고 가정 해 봅시다 Group. Group여기에 목록이 나을 수 있도록 인스턴스는, 많은 사람들이있다. 에 목록 객체를 선언하면 Group을 사용 IList<Person>하고 인스턴스로 인스턴스화합니다 List.

public class Group {
  private IList<Person> people;

  public Group() {
    this.people = new List<Person>();
  }
}

그리고 모든 것을 필요로하지 않는다면 IList항상 사용할 수도 IEnumerable있습니다. 최신 컴파일러와 프로세서를 사용하면 속도 차이가 실제로 없다고 생각하므로 스타일의 문제입니다.


3
처음에는 단순한 목록으로 만들지 않겠습니까? 나는 아직도 왜 당신이 보너스를 IList로 만들어서 얻는 지 이해하지 못합니다. 그러면 생성자에서 당신이 그것을 List <>로
만듭니다.

List <T> 객체를 명시 적으로 생성하면 인터페이스의 이점을 잃어 버리는 데 동의합니다.
The_Butcher

4

가장 일반적인 사용 가능한 유형,이 경우에는 IList 또는 더 나은 IEnumerable 인터페이스를 사용하는 것이 가장 좋습니다. 나중에 구현을 편리하게 전환 할 수 있습니다.

그러나 .NET 2.0에는 성가신 일이 있습니다. IList에는 Sort () 메서드 가 없습니다 . 제공된 어댑터를 대신 사용할 수 있습니다.

ArrayList.Adapter(list).Sort()

2

예를 들어 목록이 List 이외의 IList 구현으로 캐스트 된 경우 필요한 경우에만 인터페이스를 사용해야합니다. 예를 들어 NHibernate를 사용하면 데이터를 검색 할 때 ILists를 NHibernate 백 오브젝트로 캐스트합니다.

List가 특정 콜렉션에 사용할 유일한 구현 인 경우,이를 구체적 List 구현으로 선언하십시오.


1

일반적으로 접하는 상황에서는 IList를 직접 사용하는 경우가 거의 없습니다.

보통 나는 그것을 메소드의 인수로 사용합니다.

void ProcessArrayData(IList almostAnyTypeOfArray)
{
    // Do some stuff with the IList array
}

이렇게하면 IList가 아닌 IEnumerable을 사용하지 않는 한 .NET Framework의 거의 모든 배열에서 일반 처리를 수행 할 수 있습니다.

실제로 필요한 기능의 종류에 따라 다릅니다. 대부분의 경우 List 클래스를 사용하는 것이 좋습니다. IList는 컬렉션 내에서 캡슐화하려는 매우 구체적인 규칙을 가질 수있는 사용자 지정 배열을 만들어야 할 때 가장 좋습니다. 반복하지는 않지만 .NET에서이를 목록으로 인식하려고합니다.


1

AList 객체를 사용하면 목록을 만들고, 목록에 항목을 추가하고, 제거하고, 업데이트하고, 색인을 생성하는 등의 작업을 수행 할 수 있습니다. List는 객체 유형을 지정하는 일반 목록을 원할 때마다 사용됩니다.

반면에 IList는 인터페이스입니다. 기본적으로 BookList라는 목록 클래스와 같은 고유 한 유형의 List를 만들려면 인터페이스를 사용하여 새 클래스에 대한 기본 메서드와 구조를 제공 할 수 있습니다. IList는 List를 구현하는 고유 한 특수 하위 클래스를 만들려는 경우를위한 것입니다.

또 다른 차이점은 IList는 인터페이스이며 인스턴스화 할 수 없다는 것입니다. List는 클래스이며 인스턴스화 할 수 있습니다. 그 뜻은:

IList<string> MyList = new IList<string>();

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