Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq optimisation within a foreach

I have been looking for a way of splitting a foreach loop into multiple parts and came across the following code:

foreach(var item in items.Skip(currentPage * itemsPerPage).Take(itemsPerPage))
{
    //Do stuff
}

Would items.Skip(currentPage * itemsPerPage).Take(itemsPerPage) be processed in every iteration, or would it be processed once, and have a temporary result used with the foreach loop automatically by the compiler?

like image 759
bizzehdee Avatar asked Jul 03 '13 08:07

bizzehdee


2 Answers

No, it would be processed once.

It's the same like:

public IEnumerable<Something> GetData() {
    return someData; 
}


foreach(var d in GetData()) {
   //do something with [d]
}
like image 121
Tigran Avatar answered Nov 29 '22 02:11

Tigran


The foreach construction is equivalent to:

IEnumerator enumerator = myCollection.GetEnumerator();
try
{
   while (enumerator.MoveNext())
   {
       object current = enumerator.Current;
       Console.WriteLine(current);
   }
}
finally
{
   IDisposable e = enumerator as IDisposable;
   if (e != null)
   {
       e.Dispose();
   }
}

So, no, myCollection would be processed only once.

Update:

Please note that this depends on the implementation of the IEnumerator that the IEnumerable uses.

In this (evil) example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;


namespace TestStack
{
    class EvilEnumerator<T> : IEnumerator<T> {

        private IEnumerable<T> enumerable;
        private int index = -1;

        public EvilEnumerator(IEnumerable<T> e) 
        {
            enumerable = e;
        }


        #region IEnumerator<T> Membres

        public T Current
        {
            get { return enumerable.ElementAt(index); }
        }

        #endregion

        #region IDisposable Membres

        public void Dispose()
        {

        }

        #endregion

        #region IEnumerator Membres

        object IEnumerator.Current
        {
            get { return enumerable.ElementAt(index); }
        }

        public bool MoveNext()
        {
            index++;
            if (index >= enumerable.Count())
                return false;
            return true;
        }

        public void Reset()
        {

        }

        #endregion
    }
    class DemoEnumerable<T> : IEnumerable<T>
    {

        private IEnumerable<T> enumerable;

        public DemoEnumerable(IEnumerable<T> e)
        {
            enumerable = e; 
        }


        #region IEnumerable<T> Membres

        public IEnumerator<T> GetEnumerator()
        {
            return new EvilEnumerator<T>(enumerable);
        }

        #endregion

        #region IEnumerable Membres

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<int> numbers = Enumerable.Range(0,100);
            DemoEnumerable<int> enumerable = new DemoEnumerable<int>(numbers);
            foreach (var item in enumerable)
            {
                Console.WriteLine(item);
            }
        }
    }
}

Each iteration over enumerable would evaluate numbers two times.

like image 44
Ahmed KRAIEM Avatar answered Nov 29 '22 01:11

Ahmed KRAIEM