var list = Students.Where(s=>s.Name == "ABC");
这只会创建一个查询并且在使用查询之前不会循环元素。通过调用 ToList() 将首先执行查询,因此只循环一次您的元素。
List<Student> studentList = new List<Student>();
var list = Students.Where(s=>s.Name == "ABC");
foreach(Student s in list)
{
studentList.add(s);
}
这个例子也只会迭代一次。因为它只用过一次。请记住,该列表将在每次调用时迭代所有学生。不仅仅是那些名字是 ABC 的学生。因为它是一个查询。
对于后面的讨论,我做了一个测试示例。也许它不是 IEnumable 的最佳实现,但它做了它应该做的事情。
首先我们有我们的清单
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);
}
}
由于我们想计算调用 MoveNext 的次数,我们必须实现我们的自定义枚举器 aswel。在 MoveNext 中观察,我们的程序中有一个静态计数器。
公共类 TestEnumerator : IEnumerator { public Item FirstItem = null; 公共项目 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);
}
}
然后我们有一个自定义项目,它只是包装我们的价值
public class Item<T>
{
public Item(T item)
{
Value = item;
}
public T Value;
public Item<T> NextItem;
}
为了使用实际代码,我们创建了一个包含 3 个条目的“列表”。
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.
}
和结论?它如何影响性能?在这种情况下,MoveNext 被调用 n+1 次。不管我们有多少物品。而且 WhereClause 也无所谓,他仍然会运行 MoveNext 4 次。因为我们总是在我们的初始列表上运行我们的查询。我们将受到的唯一性能影响是实际的 LINQ 框架及其调用。实际制作的循环将是相同的。
在有人问为什么它是 N+1 次而不是 N 次之前。这是因为他最后一次没有元素时返回 false。使其成为元素的数量+列表的结尾。