IEnumerable/seq를 사용하면 안되는 이유
// C#:
int Sum(IEnumerable<int> items)
{
int total = 0;
foreach(var item in items)
{
total += item;
}
return total;
}
// F#
let sum (items: seq<int>) =
let mutable total = 0
for item in items do
total <- total + item
total
"이 함수를 호출하는 것은 영원히 중단될 수 있습니다"라고 말하면 당신이 옳을 것입니다. 다음은
IEnumerable<int>
함수에 전달할 수 있는 seq<int>
( F#의 Sum
)에 대한 유효한 값입니다.// C#
IEnumerable<int> GenerateValues()
{
while (true)
{
yield return 1;
}
}
// F#
let generateValues () =
Seq.initInfinite id
이제
Sum(GenerateValues())
가 영원히 중단됩니다.이 코드에 어떤 문제가 있습니까?
// C#
interface IConfigValuesProvider
{
IEnumerable<ConfigValue> ConfigValues { get; }
}
var values = myConfigValueProvider.ConfigValues;
if (values.Count() > 0)
{
foreach(var v in values) { ... }
}
// F#
type IConfigValuesProvider =
abstract ConfigValues : seq<ConfigValues>
let values = myConfigValueProvider.ConfigValues
if Seq.length values > 0 then
for v in values do
...
글쎄요, 아마 아무 것도 아니지만, 당신은 정말로 확신할 수 없습니다.
ConfigValues
는 IEnumerable<T>
이므로 기술적으로 무한할 수 있으므로 Count
/Seq.length
가 중단될 수 있지만 그럴 가능성은 거의 없습니다. 더 그럴듯하게 들리는 것은 ConfigValues
가 Where
또는 Select
에 대한 호출의 출력으로 구현되고 이를 반복하는 데 계산 비용이 많이 든다는 것입니다. 여기에서 (Count
를 호출한 다음 foreach
를 수행) 두 번 반복될 가능성이 있습니다. 이는 계산 비용이 많이 든다면 좋지 않습니다. 물론 호출자는 이것이 Array와 유사한 컬렉션처럼 작동할 것으로 예상하지만 실제로 IEnumerable<T>
는 그러한 약속을 하지 않습니다. 여기서 잠재적인 해결책은 ConfigValue.ToArray()
를 호출하고 그 결과를 사용하는 것이지만 ConfigValues
가 실제로 컬렉션일 경우 무의미한 복사본을 만들 것입니다. 우리는 알 수 없으므로 안전하지 않은 가정을 하거나 방어적으로 코딩하고 리소스를 낭비합니다.IEnumerable<T>
/seq<T>
는 항목 모음을 나타내지 않으므로 항목 모음을 나타내는 데 사용해서는 안 됩니다.그래서 우리는 무엇을 사용해야합니까? 모든 컬렉션이 실제로 컬렉션임을 보증하고 구현하는 또 다른 인터페이스가 있습니까? 예, 있습니다. 2012년 8월 15일 .NET 4.5에 도입된
IReadOnlyCollection<T>
입니다. 보다 구체적이고 진정한 배열과 유사한 동작(인덱싱 지원 포함)을 원하면 IReadOnlyList<T>
가 지원합니다. . 나는 인덱스가 있는 모든 것에 대해 IReadOnlyList<T>
(배열, List<T>
, ImmutableArray<T>
등) 및 IReadOnlyCollection<T>
다른 모든 것에 대해 LinkedList<T>
, Queue<T>
, Stack<T>
쪽으로 기울고 있습니다. ) ImmutableArray<T>
와 같은 구체적인 불변 컬렉션이 특히 반환 유형으로 더 나은 경우가 있습니다. 구체적인 유형을 반환하는 것이 더 큰 가치를 제공하는 것 같습니다. 예를 들어 위대하고 명확한 지침을 찾지 못했습니다. IReadOnlyList<T>
대 ImmutableArray<T>
; 그것에 대한 의견이 있으면 공유하십시오!마지막으로
IEnumerable<T>
가 컬렉션을 나타내지 않고 이를 완벽하게 대체할 수 있다면 무슨 소용이 있습니까?IEnumerable<T>
는 컬렉션, 무한 시퀀스, 연속 값 계산 등 열거를 지원하는 모든 것입니다. 임의의 무한한 데이터 시퀀스에 대한 매핑을 정의하는 데 완벽한 유형입니다. Where
도 반환하는 LINQ 연산자 Select
, IEnumerable<T>
를 생각해 보십시오. 이 연산자는 완벽하게 이해되고 더 나은 유형을 사용할 수 없습니다. IMO가 존재하지 않아야 하는 Enumerable.Sum
또는 Enumerable.Count
와 같은 스칼라 반환 함수를 생각하지 마십시오. 유한 컬렉션 외에는 의미가 없으며 작동하지도 않습니다.IReadOnlyCollection<T>
및 친구를 .NET에 늦게 추가한 것은 IEnumerable<T>
를 컬렉션의 읽기 전용 보기로 널리 사용하는 데 주로 책임이 있다고 생각합니다. 결국 IEnumerable<T>
는 2006년 초 .NET 2에, System.Linq
는 2007년 말 .NET 3.5에 도착했습니다. 같은 시기에 디자인된 F#의 Seq
모듈은 System.Linq
와 동일한 문제로 가득 차 있습니다. ) : Seq.sum
, Seq.length
, Seq.append
, Seq.toList
, Seq.toArray
등은 모두 매우 실용적이지만 확실히 건전하지 않습니다.
Reference
이 문제에 관하여(IEnumerable/seq를 사용하면 안되는 이유), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/asik/why-you-probably-shouldnt-use-ienumerableseq-3g6g텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)