답변:
다른 사람들이 사용할 라이브러리를 통해 클래스를 공개하는 경우 일반적으로 구체적인 구현이 아닌 인터페이스를 통해 클래스를 공개하려고합니다. 나중에 다른 구체적인 클래스를 사용하도록 클래스 구현을 변경하기로 결정한 경우 도움이됩니다. 이 경우 인터페이스가 변경되지 않으므로 라이브러리의 사용자가 코드를 업데이트 할 필요가 없습니다.
내부에서 사용하는 경우에는 그다지 신경 쓰지 않을 List<T>
수 있으며 사용하는 것이 좋습니다.
덜 인기있는 대답은 프로그래머가 소프트웨어를 전 세계적으로 재사용하는 척하는 것입니다. 실제로 대다수의 프로젝트가 소수의 사람들에 의해 유지되지만 멋진 인터페이스 관련 사운드 비트가 있으면 대단합니다. 당신 자신.
건축 우주 비행사 . 이미 .NET 프레임 워크에있는 항목에 추가하는 고유 한 IList를 작성할 가능성은 너무 멀기 때문에 "모범 사례"를 위해 예약 된 이론적 인 젤리 토트입니다.
인터뷰에서 어떤 것을 사용 하느냐는 질문이 있다면 IList라고 말하고 미소를 지으며 둘 다 자신이 너무 영리하다는 것을 기쁘게 생각합니다. 또는 공개 API, IList. 잘만되면 당신은 나의 요점을 얻는다.
IEnumerable<string>
하기 List<string>
위해 사용할 수있는 함수 내에서 를 반환하는 함수가 있다고 가정 하지만 호출자가 추가하거나 제거하지 않고 내용을 열거하기를 원합니다. 인터페이스를 매개 변수로 받아들이면 비슷한 문자열을 전달합니다. "문자열 모음이 필요하지만 걱정하지 않아도 변경하지 않습니다."
인터페이스는 약속 (또는 계약)입니다.
항상 약속과 함께- 작을수록 좋습니다 .
IList
약속도 마찬가지 입니다. 여기서 더 작고 더 나은 약속은 무엇입니까? 이것은 논리적이지 않습니다.
List<T>
때문에 AddRange
인터페이스에 정의되어 있지 않습니다.
IList<T>
유지할 수 없다고 약속합니다. 예를 들어, 읽기 전용 인 Add
경우에는 던질 수 있는 방법이 있습니다 IList<T>
. List<T>
반면에 항상 기록 할 수 있으므로 항상 약속을 지킵니다. 또한, .NET 4.6 이후, 우리는 지금이 IReadOnlyCollection<T>
와 IReadOnlyList<T>
이상있는 거의 항상 더 나은 적합 IList<T>
. 그리고 그들은 공변량입니다. 피하십시오 IList<T>
!
IList<T>
. 계약 자체에는 문제가 아니다 . 누군가는 IReadOnlyList<T>
모든 메소드를 구현 하고 예외를 throw 할 수 있습니다. 오리 타이핑과는 정반대입니다. 오리라고 부르지 만 오리처럼 qua 수 없습니다. 컴파일러가 강제로 실행할 수는 없지만 계약의 일부입니다. 나는 100 %가 동의 할 IReadOnlyList<T>
또는 IReadOnlyCollection<T>
하지만, 읽기 전용 액세스를 위해 사용되어야한다.
어떤 사람들은 "항상" IList<T>
대신 "을 사용 합니다 List<T>
.
그들은 당신이에서 메소드 서명을 변경하려면 void Foo(List<T> input)
에 void Foo(IList<T> input)
.
이 사람들은 틀 렸습니다.
그것보다 미묘한 차이가 있습니다. IList<T>
공용 인터페이스의 일부로 라이브러리에 반환하는 경우 향후 사용자 지정 목록을 만들 수있는 흥미로운 옵션이 있습니다. 해당 옵션이 필요하지는 않지만 논쟁의 여지가 있습니다. 구체적인 유형 대신 인터페이스를 반환하는 것이 전체 주장이라고 생각합니다. 언급 할 가치가 있지만이 경우 심각한 결함이 있습니다.
사소한 반론으로, 모든 발신자가 List<T>
어쨌든 필요하다는 것을 알 수 있으며 호출 코드는.ToList()
그러나 훨씬 더 중요한 것은, 당신은 매개 변수로 IList에 동의하는 경우 때문에 당신은 더 나은 조심 것 IList<T>
과 List<T>
같은 방식으로 작동하지 않습니다. 이름의 유사성에도 불구하고 인터페이스 공유에도 불구하고 동일한 계약을 공개 하지 않습니다 .
이 방법이 있다고 가정하십시오.
public Foo(List<int> a)
{
a.Add(someNumber);
}
도움이되는 동료가 받아 들일 방법을 "리팩터링"합니다 IList<int>
.
int[]
구현 하기 때문에 코드가 손상 IList<int>
되었지만 크기는 고정되어 있습니다. 에 대한 계약 ICollection<T>
(의베이스 IList<T>
)를 확인하는 데 사용하는 코드가 필요합니다 IsReadOnly
컬렉션에서 항목을 추가하거나 제거하기 전에 플래그. 계약은 List<T>
그렇지 않습니다.
Liskov 대체 원칙 (간체)에는 추가 전제 조건이나 사후 조건없이 파생 유형을 기본 유형 대신 사용할 수 있어야한다고 명시되어 있습니다.
이것은 Liskov 대체 원칙을 어기는 것 같습니다.
int[] array = new[] {1, 2, 3};
IList<int> ilist = array;
ilist.Add(4); // throws System.NotSupportedException
ilist.Insert(0, 0); // throws System.NotSupportedException
ilist.Remove(3); // throws System.NotSupportedException
ilist.RemoveAt(0); // throws System.NotSupportedException
그러나 그렇지 않습니다. 이에 대한 대답은 예제에서 IList <T> / ICollection <T>을 잘못 사용했다는 것입니다. ICollection <T>를 사용하는 경우 IsReadOnly 플래그를 확인해야합니다.
if (!ilist.IsReadOnly)
{
ilist.Add(4);
ilist.Insert(0, 0);
ilist.Remove(3);
ilist.RemoveAt(0);
}
else
{
// what were you planning to do if you were given a read only list anyway?
}
누군가 당신에게 Array 또는 List를 전달하면 매번 플래그를 확인하고 폴 백이 있으면 코드가 정상적으로 작동합니다 ...하지만 실제로; 누가합니까? 분석법에 추가 구성원이 필요할 수있는 목록이 필요한 경우 미리 알 수 없습니다. 메소드 서명에서 지정하지 않습니까? 다음과 같은 읽기 전용 목록을 통과 한 경우 정확히 무엇을 int[]
하시겠습니까?
/를 올바르게List<T>
사용 하는 코드로 대체 할 수 있습니다 . 당신은 할 수 없습니다 당신이 대체 할 수 있음을 보증 / 코드로 사용 .IList<T>
ICollection<T>
IList<T>
ICollection<T>
List<T>
구체적인 유형 대신 추상화를 사용한다는 주장에 대해서는 단일 책임 원칙 / 인터페이스 분리 원칙에 대한 호소력이 있습니다. 가능한 가장 좁은 인터페이스에 따라 다릅니다. 대부분의 경우 a를 List<T>
사용하는 경우 더 좁은 인터페이스를 대신 사용할 수 있다고 생각하는 이유는 IEnumerable<T>
무엇입니까? 항목을 추가 할 필요가없는 경우 종종 더 적합합니다. 컬렉션에 추가해야하는 경우 콘크리트 유형을 사용하십시오 List<T>
.
나에게 IList<T>
(그리고 ICollection<T>
)는 .NET 프레임 워크의 최악의 부분입니다. IsReadOnly
최저 놀람의 원칙을 위반합니다. Array
항목을 추가, 삽입 또는 제거 할 수없는 클래스와 같은 클래스 는 Add, Insert 및 Remove 메서드를 사용하여 인터페이스를 구현해서는 안됩니다. (또한 /software/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties 참조 )
가 IList<T>
조직에 잘 맞는가? 동료가 IList<T>
대신에 사용할 메소드 서명을 변경 List<T>
하도록 요청하는 경우,에 요소를 추가하는 방법을 물어보십시오 IList<T>
. 그들이 알지 못하는 경우 IsReadOnly
(그리고 대부분의 사람들은 모르는 경우)를 사용하지 마십시오 IList<T>
. 이제까지.
IsReadOnly 플래그는 ICollection <T>에서 가져오고 컬렉션에서 항목을 추가 또는 제거 할 수 있는지 여부를 나타냅니다. 그러나 실제로 혼란스럽게하기 위해 대체 할 수 있는지 여부는 나타내지 않으며 배열의 경우 IsReadOnlys == true를 반환 할 수 있습니다.
IsReadOnly에 대한 자세한 내용 은 ICollection <T>의 msdn 정의를 참조하십시오.
IsReadOnly
것입니다 true
을 위해 IList
, 당신은 여전히 현재 회원을 수정할 수 있습니다! 아마도 그 속성의 이름은 IsFixedSize
무엇입니까?
Array
가 AS를 IList
그래서 왜 내가 현재 회원을 수정할 수)
IReadOnlyCollection<T>
하고 IReadOnlyList<T>
있는보다 더 적합 거의 항상 IList<T>
있지만 위험한 게으른 의미가 없습니다 IEnumerable<T>
. 그리고 그들은 공변량입니다. 피하십시오 IList<T>
!
List<T>
의 특정 구현으로 IList<T>
, T[]
정수 색인을 사용하여 선형 배열과 같은 방식으로 처리 할 수있는 컨테이너입니다 . IList<T>
메소드의 인수 유형으로 지정할 때 컨테이너의 특정 기능이 필요하다는 것만 지정합니다.
예를 들어, 인터페이스 사양에서는 특정 데이터 구조를 사용하지 않습니다. List<T>
요소를 선형 배열로 액세스, 삭제 및 추가 할 때와 동일한 성능 이 구현 됩니다. 그러나 링크 된 목록으로 뒷받침되는 구현을 상상할 수 있습니다. 단, 끝에 요소를 추가하는 것이 더 저렴하지만 (일정 시간) 랜덤 액세스는 훨씬 더 비쌉니다. .NET LinkedList<T>
은 구현 하지 않습니다IList<T>
.
이 예는 또한 인수 목록에서 인터페이스가 아닌 구현을 지정해야하는 상황이있을 수 있음을 알려줍니다.이 예에서는 특정 액세스 성능 특성이 필요할 때마다. 이것은 일반적으로 컨테이너의 특정 구현에 대해 보장됩니다 ( List<T>
문서 : " IList<T>
필요에 따라 크기가 동적으로 증가하는 배열을 사용하여 일반 인터페이스를 구현합니다 ").
또한 필요한 최소한의 기능을 노출하는 것이 좋습니다. 예를 들어. 목록의 내용을 변경할 필요가 없으면 확장을 사용 IEnumerable<T>
하는 것이 IList<T>
좋습니다.
LinkedList<T>
대 복용 List<T>
대는 IList<T>
호출되는 코드에 필요한 성능 보장을 전달합니다.
구체적인 구현보다 인터페이스를 사용해야하는 이유를 정당화하는 대신 질문을 조금 돌리고 인터페이스 대신 구체적인 구현을 사용해야하는 이유를 정당화하려고합니다. 정당화 할 수 없으면 인터페이스를 사용하십시오.
TDD 및 OOP의 원칙은 일반적으로 구현이 아닌 인터페이스로 프로그래밍하는 것입니다.
이 특정 경우에는 본질적으로 사용자 정의가 아닌 언어 구성에 대해 이야기하기 때문에 일반적으로 중요하지 않지만 List가 필요한 것을 지원하지 않는다고 말하십시오. 나머지 앱에서 IList를 사용한 경우 자체 사용자 정의 클래스로 List를 확장하고 리팩토링하지 않고 계속 전달할 수 있습니다.
이 작업을 수행하는 데 드는 비용이 최소화됩니다. 나중에 두통을 피하십시오. 인터페이스 원리가 중요합니다.
이러한 List 대 IList 질문 (또는 답변) 중 어느 것도 서명 차이를 언급하지 않는다고 놀랍습니다. (왜 내가이 질문을 검색 했습니까!)
그래서 적어도 .NET 4.5 (2015 년경)부터 IList에는없는 List에 포함 된 메소드가 있습니다.
Reverse
및 ToArray
IEnumerable을 <T> 인터페이스 확장 방법은 IList <T>에서 도출이다.
List
구현 에만 의미가 있고 의 다른 구현에서는 의미가 없기 때문 입니다 IList
.
무엇 .NET의 경우 5.0을 대체 System.Collections.Generic.List<T>
하는 System.Collection.Generics.LinearList<T>
. .NET은 항상 이름을 소유 List<T>
하지만 IList<T>
계약 임을 보증합니다 . 따라서 IMHO 우리 (이스트 I)는 누군가의 이름을 사용하지 않아야하며 (이 경우 .NET이지만) 나중에 문제가 발생합니다.
을 사용하는 경우 IList<T>
호출자는 항상 작동하도록 보장되며 구현자는 기본 컬렉션을 다른 대체 구현으로 자유롭게 변경할 수 있습니다.IList
모든 개념은 기본적으로 구체적인 구현보다 인터페이스를 사용하는 이유와 관련하여 위의 대부분의 답변에 명시되어 있습니다.
IList<T> defines those methods (not including extension methods)
IList<T>
MSDN 링크
List<T>
응용 프로그램에서 사용할 방법을 고려하여 무게가 약 41 개의 공개 메소드가있는 것 외에 9 가지 메소드 (확장 메소드 제외)를 구현합니다.
List<T>
MSDN 링크
IList <>는 다른 포스터의 조언에 따라 거의 항상 바람직하지만 WCF DataContractSerializer로 직렬화 / 역 직렬화주기를 두 번 이상 반복하여 IList <>를 실행할 때 .NET 3.5 sp 1에 버그가 있습니다.
이 버그를 수정하는 SP가 있습니다 : KB 971030
인터페이스는 최소한 예상 한 메소드를 얻도록합니다 . 인터페이스의 정의를 인식하는 것, 즉 인터페이스를 상속하는 모든 클래스에 의해 구현되는 모든 추상 메소드. 따라서 어떤 사람이 인터페이스에서 상속 기능을 추가하고 다른 기능을 사용하지 않고 여러 가지 방법으로 자체 클래스를 만들면 하위 클래스에 대한 참조를 사용하는 것이 좋습니다 (이 경우 구체적인 클래스 객체를 할당하십시오.
또 다른 장점은 콘크리트 클래스의 몇 가지 메소드 만 구독하므로 코드가 콘크리트 클래스의 변경으로부터 안전하고 콘크리트 클래스가 인터페이스에서 상속하는 한 거기에있는 메소드 사용. 따라서 구체적인 구현을 작성하는 코더에게 자신의 안전을 보장하고 자신의 구체적인 클래스에 더 많은 기능을 추가하거나 추가 할 수 있습니다.
구현이 아닌 인터페이스에 대해 프로그래밍하는 순수한 OO 접근법 중 하나를 포함하여 여러 각도 에서이 주장을 볼 수 있습니다. 이러한 생각으로 IList 사용은 처음부터 정의한 인터페이스를 전달하고 사용하는 것과 동일한 원칙을 따릅니다. 또한 인터페이스가 일반적으로 제공하는 확장 성과 유연성 요소를 믿습니다. IList <T>를 구현하는 클래스를 확장하거나 변경해야하는 경우 소비 코드를 변경할 필요가 없습니다. IList Interface 계약이 준수하는 내용을 알고 있습니다. 그러나 변경되는 클래스에서 구체적인 구현과 List <T>를 사용하면 호출 코드도 변경해야 할 수 있습니다. IList <T>를 준수하는 클래스는 List <T>를 사용하는 구체적 유형에 의해 보장되지 않는 특정 동작을 보장하기 때문입니다.
또한 .Add, .Remove 또는 다른 IList 메서드를 구현하기 위해 클래스 IList <T>를 구현하는 클래스에서 List <T>의 기본 구현을 수정하는 것과 같은 작업을 수행 할 수있는 능력이 있으면 개발자에게 많은 유연성과 힘이 부여됩니다. 목록으로 <T>