Programing

IEnumerable은 왜

crosscheck 2020. 11. 12. 07:57
반응형

IEnumerable은 왜.ToList() 반환 목록 IList 대신?


확장 메서드 ToList()List<TSource>. 동일한 패턴에 ToDictionary()따라 Dictionary<TKey, TSource>.

이 메서드가 반환 값을 각각 IList<TSource>IDictionary<TKey, TSource>각각 입력하지 않는 이유가 궁금 합니다. ToLookup<TSource, TKey>실제 구현이 아닌 인터페이스로 반환 값을 입력 하기 때문에 더 이상하게 보입니다 .

dotPeek 또는 기타 디 컴파일러를 사용하는 이러한 확장 메서드의 소스를 살펴보면 다음 구현을 볼 수 있습니다 ( ToList()더 짧기 때문에 표시됨).

public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) { 
   if (source == null) throw Error.ArgumentNull("source");
   return new List<TSource>(source); 
}

그렇다면이 메서드가 반환 값을 인터페이스 자체가 아닌 인터페이스의 특정 구현으로 입력하는 이유는 무엇입니까? 유일한 변경은 반환 유형입니다.

IEnumerable<>이 두 가지 경우를 제외하고 확장이 서명에서 매우 일관 적이기 때문에 궁금합니다 . 나는 항상 그것이 조금 이상하다고 생각했습니다.

또한 상황을 더욱 혼란스럽게 만들기 위해ToLookup() 상태에 대한 문서는 다음과 같습니다.

지정된 키 선택기 함수에 따라 IEnumerable에서 Lookup을 만듭니다.

그러나 반환 유형은 ILookup<TKey, TElement>입니다.

에서 Edulinq , 존 소총은 반환 유형이 있음을 언급 List<T>하는 대신 IList<T>,하지만 더 피사체를 터치하지 않습니다.
광범위한 검색으로 답이 나오지 않았으므로 여기에서 묻습니다.

반환 값을 인터페이스로 입력하지 않는 디자인 결정이 있습니까? 아니면 우연입니까?


귀국 List<T>List<T>일부가 아닌 방법을 IList<T>쉽게 사용할 수 있다는 장점 이 있습니다. 로 할 수있는 작업이 많이 List<T>있지만 IList<T>.

반대로 에는 ( ) 가없는 Lookup<TKey, TElement>사용 가능한 메서드가 하나만 있으며 어쨌든 사용하지 않을 것입니다.ILookup<TKey, TElement>ApplyResultSelector


이러한 종류의 결정은 임의적으로 느껴질 수 있지만 둘 다 구현 하기 때문에 인터페이스보다는 ToList()반환 하지만 일반 유형의 개체에 존재하지 않는 다른 구성원을 추가 합니다.List<T>List<T>IList<T>IList<T>

예 : AddRange().

무엇 IList<T>을 구현 해야하는지 확인하세요 ( http://msdn.microsoft.com/en-us/library/5y536ey6.aspx ) :

public interface IList<T> : ICollection<T>, 
    IEnumerable<T>, IEnumerable

그리고 List<T>( http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx ) :

public class List<T> : IList<T>, ICollection<T>, 
    IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable

어쩌면 자신의 코드가 필요하지 않습니다 IReadOnlyList<T>, IReadOnlyCollection<T>또는 ICollection하지만, .NET 프레임 워크 및 기타 제품에 대한 다른 구성 요소는보다 전문 목록 객체에 의존 할 수와 그의 이유는 .NET dev에 팀 인터페이스를 반환하지 않습니다하기로 결정했다.

항상 인터페이스를 반환하는 것이 모범 사례 라고 생각하지 마십시오 . 코드 또는 타사 코드에 이러한 캡슐화가 필요한 경우입니다.


List오버를 갖는 것에는 여러 가지 이점이 있습니다 IList. 우선, 그렇지 않은 List방법이 IList있습니다. 또한 어떻게 동작할지 추론 할 수있는 구현이 무엇인지 알고 있습니다. 끝 부분에는 효율적으로 추가 할 수 있지만 시작 부분에는 추가 할 수 없으며 인덱서가 매우 빠르다는 것을 알고 있습니다.

구조가 a로 변경되어 LinkedList애플리케이션 성능이 저하 되는 것에 대해 걱정할 필요가 없습니다 . 이와 같은 데이터 구조에 관해서는 데이터 구조가 따르는 계약뿐만 아니라 데이터 구조가 어떻게 구현 되는지 아는 것이 상당히 많은 상황에서 매우 중요 합니다. 절대 변해서는 안되는 행동입니다.

IList또한를받는 메서드에를 전달할 수 없습니다 List. 이는 상당히 많이 볼 수 있습니다. ToList사람이 List제어 할 수없는 서명과 일치 시키기 위해 실제로의 인스턴스가 필요하기 때문에 자주 사용됩니다 IList.

그런 다음 돌아 오면 어떤 이점이 있는지 자문 해 봅니다 IList. 글쎄, 우리는 목록의 다른 구현을 반환 할 수 있지만, 앞에서 언급했듯이 이것은 매우해로운 결과, 거의 확실하게 다른 유형을 사용하여 얻을 수있는 것보다 훨씬 더 많습니다. 구현 대신 인터페이스를 사용하는 것은 따뜻한 모호함을 줄 수 있지만,이 맥락에서 (일반적으로 또는) 좋은 사고 방식이라고 생각하지 않습니다. 일반적으로 인터페이스를 반환하는 것은 구체적인 구현을 반환하는 것보다 일반적으로 바람직하지 않습니다. "당신이 받아들이는 것에 대해 자유롭고 당신이 제공하는 것에 구체적이십시오." 메서드에 대한 매개 변수는 가능한 경우 호출자가 필요한 작업을 수행하는 모든 구현에서 전달할 수있는 최소한의 기능을 정의하는 인터페이스 여야하며 호출자처럼 구체적인 구현을 제공해야합니다. 그 개체가 할 수있는 한 결과로 많은 것을 할 수 있도록 볼 수 있도록 "허용"됩니다.

So now we move onto, "Why return ILookup and not Lookup?" Well, first off Lookup isn't a public class. There is no Lookup in System.Collections.*. The Lookup class that is exposed through LINQ exposes no constructors publicly. You're not able to use the class except through ToLookup. It also exposes no functionality that isn't already exposed through ILookup. In this particular case they designed the interface specifically around this exact method (ToLookup) and the Lookup class is a class specifically designed to implement that interface. Because of all of this virtually all of the points discussed about List just don't apply here. Would it have been a problem to return Lookup instead, no, not really. In this case it really just doesn't matter much at all either way.


In my opinion returning a List<T> is justified by the fact that the method name says ToList. Otherwise it would have to be named ToIList. It is the very purpose of this method to convert an unspecific IEnumerable<T> to the specific type List<T>.

If you had a method with an unspecific name like GetResults, then a return type like IList<T> or IEnumerable<T> would seem appropriate to me.


If you look at the implementation of the Lookup<TKey, TElement> class with reflector, you'll see a lot of internal members, that are only accessible to LINQ itself. There is no public constructor and Lookup objects are immutable. Therefore there would be no advantage in exposing Lookup directly.

Lookup<TKey, TElement> class seems to be kind of LINQ-internal and is not meant for public use.


In general when you call ToList() on a method you're looking for a concrete type otherwise the item could stay as type IEnumerable. You don't need to convert to a List unless you're doing something that requires a concrete list.


I believe that the decision to return a List<> instead of an IList<> is that one of the more common use cases for calling ToList is to force immediate evaluation of the entire list. By returning a List<> this is guaranteed. With an IList<> the implementation can still be lazy, which would defeat the "primary" purpose of the call.


Because List<T> actually implements a range of interfaces, not just IList:

public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable{

}

Each of those interfaces define a range of features which the List must conform. Picking one particular one, would render bulk of the implementation unusable.

If you do want to return IList, nothing stops you from having your own simple wrapper:

public static IList<TSource> ToIList<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException(source);
    return source.ToList();
}

The short answer is that in general returning the most specific type available is recommended by the authoritative Framework Design Guidelines. (sorry I don't have a citation on hand, but I remember this clearly since it stuck out in contrast to the Java community guidelines which prefer the opposite).

This makes sense to me. You can always do e.g. IList<int> list = x.ToList(), only the library author needs to be concerned with being able to support the concrete return type.

ToLookup<T> is the unique one in the crowd. But perfectly within the guidelines: it is the most specific type available that the library authors are willing to support (as others have pointed out, the concrete Lookup<T> type appears to be more of an internal type not meant for public use).


This is one of the common things that programmers have difficulty understanding around the use of interfaces and concrete types.

Returning a concrete List<T> that implements IList<T> only gives the method consumer more information. Here is what the List object implements (via MSDN):

[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, 
    IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable

Returning as a List<T> gives us the ability to call members on all of these interfaces in addition to List<T> itself. For example we could only use List.BinarySearch(T) on a List<T>, as it exists in List<T> but not in IList<T>.

In general to maximize flexibility of our methods, we should take the most abstract types as parameters (ie. only the things we're going to use) and return the least abstract type possible (to allow a more functional return object).


If a function returns a newly-constructed immutable object, the caller should generally not care about the precise type returned provided it is capable of holding the actual data that it contains. For example, a function that is supposed to return an IImmutableMatrix might normally return an ImmutableArrayMatrix backed by a privately-held array, but if all the cells hold zeroes it might instead return an ZeroMatrix, backed only by Width and Height fields (with a getter that simply returns zero all the time). The caller wouldn't care whether it was given an ImmutableArrayMatrix matrix or a ZeroMatrix; both types would would allow all of their cells to be read, and guarantee their values would never change, and that's what the caller would care about.

On the other hand, functions that return newly-constructed objects that allow open-ended mutation should generally return the precise type the caller is going to expect. Unless there will be a means by which the caller can request different return types (e.g. by calling ToyotaFactory.Build("Corolla") versus ToyotaFactory.Build("Prius")) there's no reason for the declared return type to be anything else. While factories that return immutable data-holding objects can select a type based on the data to be contained, factories that return freely-mutable types will have no way of knowing what data may be put into them. If different callers will have different needs (e.g. returning to the extant example, some callers' needs would be met with an array, while others' would not) they should be given a choice of factory methods.

BTW, something like IEnumerator<T>.GetEnumerator() is a bit of a special case. The returned object will almost always be mutable, but only in a very highly-constrained fashion; indeed, it is expected that the returned object regardless of its type will have exactly one piece of mutable state: its position in the enumeration sequence. Although an IEnumerator<T> is expected to be mutable, the portions of its state which would vary in derived-class implementations are not.

참고URL : https://stackoverflow.com/questions/14753205/why-does-ienumerablet-tolistt-return-listt-instead-of-ilistt

반응형