use*_*469 13 c# linq performance
如果我没有错,ToList()方法会迭代所提供集合的每个元素,并将它们添加到List的新实例并返回此实例.假设示例
//using linq
list = Students.Where(s => s.Name == "ABC").ToList();
//traditional way
foreach (var student in Students)
{
if (student.Name == "ABC")
list.Add(student);
}
Run Code Online (Sandbox Code Playgroud)
我认为传统的方法更快,因为它只循环一次,其中Linq的上面迭代两次一次用于Where方法然后用于ToList()方法.
我正在研究的项目现在已经广泛使用了Lists,我发现如果我将列表变量作为IEnumerable并删除.ToList,有很多类似的使用ToList()和其他方法可以像上面那样做得更好. ()并将其作为IEnumerable进一步使用.
这些事情会对性能产生什么影响?
svi*_*ick 11
这些事情会对性能产生什么影响?
这取决于你的代码.大多数情况下,使用LINQ确实会导致性能下降.在某些情况下,这个命中对您来说很重要,但是只有当您知道它对您来说太慢时才应该避免使用LINQ(即,如果对代码进行分析表明LINQ是您的代码速度慢的原因).
但是你ToList()经常使用会导致严重的性能问题.你应该ToList()只在必要时打电话.请注意,在某些情况下,添加ToList()可以大大提高性能(例如,每次迭代时从数据库加载集合时).
关于迭代次数:它取决于"迭代两次"究竟是什么意思.如果你计算MoveNext()一些集合上调用的次数,那么是的,使用Where()这种方式导致迭代两次.操作顺序如下(为了简化,我将假设所有项都符合条件):
Where()被调用,现在没有迭代,Where()返回一个特殊的可枚举.ToList()被调用,调用MoveNext()从中返回的枚举Where().Where()现在调用MoveNext()原始集合并获取值.Where()调用你的谓词,它返回true.MoveNext()从ToList()return 调用,ToList()获取值并将其添加到列表中.这意味着,如果所有ñ原来集合中的项目符合条件,MoveNext()将被称为2 ñ倍,ñ时间从Where()和ñ从时间ToList().
var list = Students.Where(s=>s.Name == "ABC");
Run Code Online (Sandbox Code Playgroud)
这只会创建一个查询,并且在使用该查询之前不会循环元素。通过调用 ToList() 将首先执行查询,因此只循环您的元素一次。
List<Student> studentList = new List<Student>();
var list = Students.Where(s=>s.Name == "ABC");
foreach(Student s in list)
{
studentList.add(s);
}
Run Code Online (Sandbox Code Playgroud)
这个例子也只会迭代一次。因为它只用过一次。请记住,该列表将在每次调用时迭代所有学生。不仅仅是那些名字是 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);
}
}
Run Code Online (Sandbox Code Playgroud)
由于我们想要计算 MoveNext 被调用的次数,我们必须实现我们的自定义枚举器 aswel。在 MoveNext 中观察,我们的程序中有一个静态计数器。
公共类 TestEnumerator : IEnumerator { public Item FirstItem = null; 公共项目当前项目 = 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);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我们有一个自定义项目,它只是包装我们的价值
public class Item<T>
{
public Item(T item)
{
Value = item;
}
public T Value;
public Item<T> NextItem;
}
Run Code Online (Sandbox Code Playgroud)
为了使用实际代码,我们创建了一个包含 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.
}
Run Code Online (Sandbox Code Playgroud)
和结论?它如何影响性能?在这种情况下,MoveNext 被调用 n+1 次。不管我们有多少物品。并且 WhereClause 也无关紧要,他仍然会运行 MoveNext 4 次。因为我们总是在我们的初始列表上运行我们的查询。我们将采取的唯一性能影响是实际的 LINQ 框架及其调用。实际制作的循环将是相同的。
在有人问为什么是 N+1 次而不是 N 次之前。这是因为他最后一次在元素不足时返回false。使其成为元素数 + 列表结尾。