IEnumerable/seq를 사용하면 안되는 이유

7828 단어 dotnetcsharpfsharp
이 코드에 어떤 문제가 있습니까?

// 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
        ...


글쎄요, 아마 아무 것도 아니지만, 당신은 정말로 확신할 수 없습니다. ConfigValuesIEnumerable<T> 이므로 기술적으로 무한할 수 있으므로 Count/Seq.length 가 중단될 수 있지만 그럴 가능성은 거의 없습니다. 더 그럴듯하게 들리는 것은 ConfigValuesWhere 또는 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 등은 모두 매우 실용적이지만 확실히 건전하지 않습니다.

좋은 웹페이지 즐겨찾기