다음과 같은 질문에 대한 답변 : List <T> 또는 IList <T>는 항상 인터페이스를 반환하는 것이 컬렉션의 구체적인 구현을 반환하는 것보다 낫다는 데 동의하는 것 같습니다. 그러나 나는 이것으로 어려움을 겪고 있습니다. 인터페이스를 인스턴스화하는 것은 불가능하므로 메서드가 인터페이스를 반환하는 경우 실제로는 여전히 특정 구현을 반환합니다. 두 가지 작은 방법을 작성하여 이것을 약간 실험했습니다.
public static IList<int> ExposeArrayIList()
{
return new[] { 1, 2, 3 };
}
public static IList<int> ExposeListIList()
{
return new List<int> { 1, 2, 3 };
}
그리고 내 테스트 프로그램에서 사용하십시오.
static void Main(string[] args)
{
IList<int> arrayIList = ExposeArrayIList();
IList<int> listIList = ExposeListIList();
//Will give a runtime error
arrayIList.Add(10);
//Runs perfectly
listIList.Add(10);
}
두 경우 모두 새 값을 추가하려고 할 때 컴파일러에서 오류가 발생하지 않지만 배열을로 노출하는 메서드는 내가 IList<T>
뭔가를 추가하려고 할 때 런타임 오류를 발생시킵니다. 그래서 내 방법에서 무슨 일이 일어나고 있는지 모르고 여기에 값 을 추가해야하는 사람들은 오류 위험없이 값을 추가 할 수 있도록 먼저 my IList
를 a List
에 복사해야 합니다. 물론 그들은과 그들이 경우있는 거 거래 볼 수있는 유형 체킹을 수행 할 수 있습니다 List
또는를 Array
하지만 그렇게하지 않으면, 그들은 컬렉션에 항목을 추가 할 그들이 다른 선택의 여지가 복사되지해야 IList
A를 List
하더라도, 이미List
. 배열은 절대로 노출되지 않아야 IList
합니까?
나의 또 다른 관심사는 연결된 질문 (강조 내) 에 대한 수용된 답변에 기반합니다 .
다른 사람들이 사용할 라이브러리를 통해 클래스를 노출하는 경우 일반적으로 구체적인 구현이 아닌 인터페이스를 통해 클래스 를 노출하려고합니다. 나중에 다른 구체적인 클래스를 사용하기 위해 클래스 구현을 변경하기로 결정한 경우 도움이됩니다. 이 경우 인터페이스가 변경되지 않으므로 라이브러리 사용자는 코드를 업데이트 할 필요가 없습니다.
내부적으로 만 사용하는 경우별로 신경 쓰지 않을 수 있으며 List를 사용해도 괜찮을 수 있습니다.
누군가가 실제로 값을 추가 / 제거하기 위해 IList<T>
내 ExposeListIlist()
방법 에서 얻은 내 방법을 사용했다고 상상해보십시오 . 모든 것이 잘 작동합니다. 그러나 이제 대답에서 알 수 있듯이 인터페이스를 반환하는 것이 더 유연하기 때문에 List 대신 배열을 반환합니다 (내쪽에는 문제가 없습니다!).
TLDR :
1) 인터페이스를 노출하면 불필요한 캐스트가 발생합니까? 상관 없어?
2) 때때로 라이브러리 사용자가 캐스트를 사용하지 않으면 메서드가 완벽하게 유지 되더라도 메서드를 변경할 때 코드가 손상 될 수 있습니다.
나는 아마도 이것을 지나치게 생각하고 있지만, 구현을 반환하는 것보다 인터페이스를 반환하는 것이 선호된다는 일반적인 합의를 얻지 못했습니다.
답변
이것이 귀하의 질문에 직접적으로 대답하는 것은 아니지만 .NET 4.5 이상에서는 공개 또는 보호 된 API를 설계 할 때 다음 규칙을 따르는 것이 좋습니다.
IEnumerable<T>
열거 형 만 사용할 수있는 경우 return을 수행 합니다.IReadOnlyCollection<T>
열거 및 항목 수를 모두 사용할 수 있으면 반환 하십시오.IReadOnlyList<T>
열거, 항목 수 및 인덱싱 된 액세스를 사용할 수있는 경우 return을 수행 합니다.ICollection<T>
열거, 항목 수 및 수정이 가능하면 반환 하십시오.IList<T>
열거, 항목 수, 인덱싱 된 액세스 및 수정을 사용할 수있는 경우 반환 합니다.
마지막 두 옵션은 해당 메서드가 IList<T>
구현으로 배열을 반환해서는 안된다고 가정합니다 .
답변
아니요, 소비자는 IList 가 정확히 무엇인지 알아야합니다 .
IList는 ICollection 인터페이스의 자손이며 모든 비 제네릭 목록의 기본 인터페이스입니다. IList 구현은 읽기 전용, 고정 크기 및 가변 크기의 세 가지 범주로 나뉩니다. 읽기 전용 IList는 수정할 수 없습니다. 고정 크기 IList는 요소의 추가 또는 제거를 허용하지 않지만 기존 요소의 수정은 허용합니다. 가변 크기 IList를 사용하면 요소를 추가, 제거 및 수정할 수 있습니다.
당신은 확인할 수 IList.IsFixedSize
와 IList.IsReadOnly
당신이 그것으로 원하는 일을.
나는 IList
팻 인터페이스의 예 라고 생각 하며 여러 개의 작은 인터페이스로 분할되어야 하며 배열을 .NET으로 반환 할 때 Liskov 대체 원칙을 위반 합니다 IList
.
인터페이스 반환에 대한 결정을 내리려면 더 읽어보세요.
최신 정보
더 파고 나는 그 발견 IList<T>
구현하지 않습니다 IList
및 IsReadOnly
기본 인터페이스를 통해 액세스 할 수 ICollection<T>
있지만 거기 IsFixedSize
에 대해 IList<T>
. 제네릭 IList <>가 제네릭이 아닌 IList를 상속하지 않는 이유 에 대해 자세히 알아보십시오 .
답변
모든 “인터페이스 대 구현”질문과 마찬가지로 공용 멤버 노출이 의미하는 바를 이해해야합니다.이 클래스의 공용 API를 정의합니다.
당신이 노출되면 List<T>
멤버 (필드, 재산, 방법, …)로, 해당 회원의 소비자에게이 방법을 액세스하여 얻은 유형 입니다List<T>
그의 유래, 또는 뭔가.
이제 인터페이스를 노출하면 구체적인 유형을 사용하여 클래스의 “구현 세부 정보”를 숨 깁니다. 물론 당신은 인스턴스화 할 수 없습니다 IList<T>
,하지만 당신은을 사용할 수 있습니다 Collection<T>
, List<T>
, 유도 또는 그 자신의 유형 구현 IList<T>
.
실제 질문은 “왜 않습니다 Array
구현 IList<T>
“ , 또는 “왜는이 IList<T>
많은 회원 그래서 인터페이스를” .
또한 해당 구성원의 소비자가 원하는 작업에 따라 다릅니다. 실제로 회원을 통해 내부 회원을 반환하는 경우, 어쨌든를 반환하고 Expose...
싶을 것 new List<T>(internalMember)
입니다. 그렇지 않으면 소비자가이를 IList<T>
통해 내부 회원을 캐스트 하고 수정할 수 있기 때문입니다.
소비자가 결과를 반복하기를 기대한다면 IEnumerable<T>
또는 IReadOnlyCollection<T>
대신 노출하십시오 .
답변
문맥에서 벗어난 담요 따옴표에주의하십시오.
인터페이스를 반환하는 것이 구체적인 구현을 반환하는 것보다 낫습니다.
이 인용문은 SOLID 원칙 의 맥락에서 사용되는 경우에만 의미가 있습니다 . 5 가지 원칙이 있지만이 논의의 목적을 위해 마지막 3 가지에 대해서만 이야기하겠습니다.
종속성 반전 원리
하나는“추상화에 의존해야합니다. 결심에 의존하지 마십시오.”
제 생각에는이 원칙이 가장 이해하기 어렵습니다. 그러나 견적을주의 깊게 살펴보면 원래 견적과 매우 흡사합니다.
인터페이스 (추상화)에 따라 다릅니다. 구체적인 구현 (콘크리트)에 의존하지 마십시오.
이것은 여전히 약간 혼란 스럽지만 다른 원칙을 함께 적용하기 시작하면 훨씬 더 이해하기 시작합니다.
Liskov 대체 원리
“프로그램의 객체는 해당 프로그램의 정확성을 변경하지 않고 하위 유형의 인스턴스로 대체 할 수 있어야합니다.”
지적했듯이을 반환하는 Array
것은 List<T>
둘 다 구현하더라도 a를 반환하는 것과 분명히 다른 동작 IList<T>
입니다. 이것은 가장 확실한 LSP 위반입니다.
깨달아야 할 중요한 것은 인터페이스가 소비자 에 관한 것이라는 것 입니다. 인터페이스를 반환하는 경우 프로그램의 동작을 변경하지 않고 해당 인터페이스의 모든 메서드 또는 속성을 사용할 수 있다는 계약을 만든 것입니다.
인터페이스 분리 원칙
“많은 클라이언트 별 인터페이스가 하나의 범용 인터페이스보다 낫습니다.”
인터페이스를 반환하는 경우 구현에서 지원하는 가장 클라이언트 별 인터페이스를 반환해야합니다. 즉, 클라이언트가 Add
메서드 를 호출 할 것으로 기대하지 않는 경우 메서드가있는 인터페이스를 반환해서는 안됩니다 Add
.
안타깝게도 .NET 프레임 워크 (특히 초기 버전)의 인터페이스가 항상 이상적인 클라이언트 특정 인터페이스는 아닙니다. @Dennis가 그의 답변에서 지적했듯이 .NET 4.5 이상에는 더 많은 선택 사항이 있습니다.
답변
인터페이스를 반환하는 것이 컬렉션의 구체적인 구현을 반환하는 것보다 반드시 낫지는 않습니다. 구체적인 유형 대신 인터페이스를 사용해야하는 좋은 이유가 항상 있어야합니다. 귀하의 예에서는 그렇게하는 것이 무의미한 것 같습니다.
인터페이스를 사용하는 유효한 이유는 다음과 같습니다.
-
인터페이스를 반환하는 메서드의 구현이 어떻게 생겼는지 알 수 없으며 시간이 지남에 따라 개발 될 수 있습니다. 다른 회사에서 작성한 다른 사람 일 수 있습니다. 따라서 필요한 사항에 동의하고 기능을 구현하는 방법은 그들에게 맡기면됩니다.
-
유형이 안전한 방식으로 클래스 계층 구조와 독립적 인 몇 가지 공통 기능을 노출하려고합니다. 동일한 메서드를 제공해야하는 다른 기본 유형의 개체는 인터페이스를 구현합니다.
1과 2는 기본적으로 같은 이유라고 주장 할 수 있습니다. 궁극적으로 동일한 요구로 이어지는 두 가지 시나리오입니다.
“계약입니다”. 계약이 본인과 있고 애플리케이션이 기능과 시간 모두에서 종료 된 경우 인터페이스를 사용할 필요가없는 경우가 많습니다.