var list = Students.Where(s=>s.Name == "ABC");
This will only create a query and not loop the elements until the query is used. By calling ToList() will first then execute the query and thus only loop your elements once.
List<Student> studentList = new List<Student>(); var list = Students.Where(s=>s.Name == "ABC"); foreach(Student s in list) { studentList.add(s); }
this example will also only iterate once. Because its only used once. Keep in mind that list will iterate all students everytime its called.. Not only just those whose names are ABC. Since its a query.
And for the later discussion Ive made a testexample. Perhaps its not the very best implementation of IEnumable but it does what its supposed to do.
First we have our list
public class TestList<T> : IEnumerable<T> { private TestEnumerator<T> _Enumerator; public TestList() { _Enumerator = new TestEnumerator<T>(); } public IEnumerator<T> GetEnumerator() { return _Enumerator; } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new NotImplementedException(); } internal void Add(T p) { _Enumerator.Add(p); } }
And since we want to count how many times MoveNext is called we have to implement our custom enumerator aswel. Observe in MoveNext we have a counter that is static in our program.
public class TestEnumerator : IEnumerator { public Item FirstItem = null; public Item CurrentItem = null;
public TestEnumerator() { } public T Current { get { return CurrentItem.Value; } } public void Dispose() { } object System.Collections.IEnumerator.Current { get { throw new NotImplementedException(); } } public bool MoveNext() { Program.Counter++; if (CurrentItem == null) { CurrentItem = FirstItem; return true; } if (CurrentItem != null && CurrentItem.NextItem != null) { CurrentItem = CurrentItem.NextItem; return true; } return false; } public void Reset() { CurrentItem = null; } internal void Add(T p) { if (FirstItem == null) { FirstItem = new Item<T>(p); return; } Item<T> lastItem = FirstItem; while (lastItem.NextItem != null) { lastItem = lastItem.NextItem; } lastItem.NextItem = new Item<T>(p); } }
And then we have a custom item that just wraps our value
public class Item<T> { public Item(T item) { Value = item; } public T Value; public Item<T> NextItem; }
To use the actual code we create a "list" with 3 entries.
public static int Counter = 0; static void Main(string[] args) { TestList<int> list = new TestList<int>(); list.Add(1); list.Add(2); list.Add(3); var v = list.Where(c => c == 2).ToList(); //will use movenext 4 times var v = list.Where(c => true).ToList(); //will also use movenext 4 times List<int> tmpList = new List<int>(); //And the loop in OP question foreach(var i in list) { tmpList.Add(i); } //Also 4 times. }
And conclusion? How does it hit performance? The MoveNext is called n+1 times in this case. Regardless of how many items we have. And also the WhereClause does not matter, he will still run MoveNext 4 times. Because we always run our query on our initial list. The only performance hit we will take is the actual LINQ framework and its calls. The actual loops made will be the same.
And before anyone asks why its N+1 times and not N times. Its because he returns false the last time when he is out of elements. Making it the number of elements + end of list.
Whereas anif-clause in a loop, not as a loop. Actually theWherewill be used whenToListenumerates the sequence.