1

I have such dummy code:

public int SAdd2(IEnumerable < int > a)
{
    var sum = a.Sum();
    var s = a.Where(x = > x < 4).Sum();
    return sum;
}

Resharper sugest me to change it to:

public int SAdd(IEnumerable < int > a)
{
    var enumerable = a as int[] ? ? a.ToArray();
    var sum = enumerable.Sum();
    var s = enumerable.Where(x = > x < 4).Sum();
    return sum;
}

Or the same with List<int> instead of array.

Because:

Possible multiple enumeration of IEnumerable

I found on MS Sources code for Sum in Enumerable

public static int Sum(this IEnumerable < int > source)
{
    if (source == null) throw Error.ArgumentNull("source");
    int sum = 0;
    checked
    {
        foreach(int v in source) sum += v;
    }
    return sum;
}

On site sharplab.io I generate IL code:

.method public hidebysig
instance int32 SAdd(
    class[mscorlib] System.Collections.Generic.IEnumerable` 1 < int32 > a
) cil managed
{
    // Method begins at RVA 0x2054
    // Code size 67 (0x43)
    .maxstack 3
        .locals init(
            [0] int32
    )
    IL_0000: ldarg.1
    IL_0001: isinst int32[]
    IL_0006: dup
    IL_0007: brtrue.s IL_0010
    IL_0009: pop
    IL_000a: ldarg.1
    IL_000b: call !! 0[][System.Core] System.Linq.Enumerable::ToArray < int32 > (class[mscorlib] System.Collections.Generic.IEnumerable` 1 < !! 0 > )
    IL_0010: dup
    IL_0011: call int32[System.Core] System.Linq.Enumerable::Sum(class[mscorlib] System.Collections.Generic.IEnumerable` 1 < int32 > )
    IL_0016: stloc.0
    IL_0017: ldsfld class[mscorlib] System.Func` 2 < int32,
    bool > C / '<>c'::'<>9__1_0'
    IL_001c: dup
    IL_001d: brtrue.s IL_0036
    IL_001f: pop
    IL_0020: ldsfld class C / '<>c'
    C / '<>c'::'<>9'
    IL_0025: ldftn instance bool C / '<>c'::'<SAdd>b__1_0' (int32)
    IL_002b: newobj instance void class[mscorlib] System.Func` 2 < int32,
    bool > ::.ctor(object, native int)
    IL_0030: dup
    IL_0031: stsfld class[mscorlib] System.Func` 2 < int32,
    bool > C / '<>c'::'<>9__1_0'
    IL_0036: call class[mscorlib] System.Collections.Generic.IEnumerable` 1 < !! 0 > [System.Core] System.Linq.Enumerable::Where < int32 > (class[mscorlib] System.Collections.Generic.IEnumerable` 1 < !! 0 > , class[mscorlib] System.Func` 2 < !! 0, bool > )
    IL_003b: call int32[System.Core] System.Linq.Enumerable::Sum(class[mscorlib] System.Collections.Generic.IEnumerable` 1 < int32 > )
    IL_0040: pop
    IL_0041: ldloc.0
    IL_0042: ret
} // end of method C::SAdd
.method public hidebysig
instance int32 SAdd2(
    class[mscorlib] System.Collections.Generic.IEnumerable` 1 < int32 > a
) cil managed
{
    // Method begins at RVA 0x20a3
    // Code size 50 (0x32)
    .maxstack 8
    IL_0000: ldarg.1
    IL_0001: call int32[System.Core] System.Linq.Enumerable::Sum(class[mscorlib] System.Collections.Generic.IEnumerable` 1 < int32 > )
    IL_0006: ldarg.1
    IL_0007: ldsfld class[mscorlib] System.Func` 2 < int32,
    bool > C / '<>c'::'<>9__2_0'
    IL_000c: dup
    IL_000d: brtrue.s IL_0026
    IL_000f: pop
    IL_0010: ldsfld class C / '<>c'
    C / '<>c'::'<>9'
    IL_0015: ldftn instance bool C / '<>c'::'<SAdd2>b__2_0' (int32)
    IL_001b: newobj instance void class[mscorlib] System.Func` 2 < int32,
    bool > ::.ctor(object, native int)
    IL_0020: dup
    IL_0021: stsfld class[mscorlib] System.Func` 2 < int32,
    bool > C / '<>c'::'<>9__2_0'
    IL_0026: call class[mscorlib] System.Collections.Generic.IEnumerable` 1 < !! 0 > [System.Core] System.Linq.Enumerable::Where < int32 > (class[mscorlib] System.Collections.Generic.IEnumerable` 1 < !! 0 > , class[mscorlib] System.Func` 2 < !! 0, bool > )
    IL_002b: call int32[System.Core] System.Linq.Enumerable::Sum(class[mscorlib] System.Collections.Generic.IEnumerable` 1 < int32 > )
    IL_0030: pop
    IL_0031: ret
} // end of method C::SAdd2

In both cases is called the same function:

call int32 [System.Core]System.Linq.Enumerable::Sum(class [mscorlib]System.Collections.Generic.IEnumerable`1<int32>)

And why version with array or List is better than version with IEnumerable

BWA
  • 5,672
  • 7
  • 34
  • 45
  • If you want to know whether to use `ToList` or `ToArray` then I suggest you do a benchmark. – Jeroen Vannevel Dec 29 '17 at 12:39
  • By *multiple enumeration of IEnumerable* it targets your variable `a`, which will be enumerated twice by the first `Sum()` call and the second `Where().Sum()` call. Imagine `a` is actually `IQueryable` pointing to db query. The db query wil be executed twice. While with the suggested edit it will be executed once. – Ivan Stoev Dec 29 '17 at 12:40
  • @JeroenVannevel I want to know why `ToLIst` or `ToArrray` instead `IEnumerable` – BWA Dec 29 '17 at 12:41
  • 1
    @BWA In that case the question has been asked many times before and you should google for it instead. – Jeroen Vannevel Dec 29 '17 at 12:43
  • @JeroenVannevel I googled but must ask google wrong question, thanks. – BWA Dec 29 '17 at 12:43
  • 1
    Note that suggested "improvement" is not always applicable. Sometimes you really want to enumerate same enumerable twice and not buffer all data (which might be huge). If take for example IQueryable from above comment - suggested improvement will bring whole db table into memory and sum it there. Doing 2 database queries instead will be _much_ faster and efficient than that (and Resharper will not suggest you to do such bad thing fortunately, if you pass `IQueryable` directly and not `IEnumerable`). – Evk Dec 29 '17 at 12:56
  • @Evk good point – BWA Dec 29 '17 at 13:04
  • 1
    @Evk It's not just about performance. Enumerables are not required (thus not guaranteed) to return same result when enumerated - for instance enumerable returning random numbers. – Ivan Stoev Dec 29 '17 at 13:08

0 Answers0