我最近找到了LINQ并喜欢它。我发现很多场合使用它比速记版本更具表现力,但是一位同事传达了关于我滥用这项技术的评论,现在让我自己猜测。我的观点是,如果一项技术有效运作且代码优雅,那么为什么不使用呢?那是错的吗?我可以花费额外的时间来编写“longhand”进程,虽然生成的代码可能快几毫秒,但是代码的数量是代码的2-3倍,因此可能存在错误的可能性增加2-3倍。
我的观点错了吗? 应该我是用手写代码而不是使用LINQ吗?这不是LINQ的设计目的吗?
编辑:我说的是LINQ to objects,我没有使用LINQ to XML这么多而且我使用过LINQ to SQL但我并不那么喜欢LINQ to objects这些风格。答案 0 :(得分:19)
我必须同意你的观点 - 如果它的写作和优雅更有效,那么几毫秒。编写额外的代码为错误提供了更多的空间,并且需要测试的是额外的代码,而且最重要的是需要维护的额外代码。想想那些将要进入你身边的人并维护你的代码 - 他们会感谢你在感谢你编写快几毫秒的代码之前写出优雅易读的代码!
请注意,当您考虑到更大的影响时,这几毫秒的成本可能会很大。如果几毫秒是数千次重复循环的一部分,那么毫秒加起来很快。
答案 1 :(得分:9)
是的,你可以过多地爱LINQ - Single Statement LINQ RayTracer
你在哪里划线?我会说使用LINQ,因为它使代码更简单,更容易阅读。
当LINQ版本变得更难以理解时,非LINQ版本是时候交换,反之亦然。编辑:这主要适用于LINQ-To-Objects,因为其他LINQ风格有其自身的好处。
答案 2 :(得分:8)
不可能太过爱Linq对象太多了,这是一个非常棒的技术!
但严重的是,任何使你的代码易于阅读,易于维护并完成预期工作的东西,那么你就不会尽可能多地使用它。
答案 3 :(得分:5)
LINQ应该用于对来自各种来源的数据进行过滤,排序,聚合和操作,尽可能直观和富有表现力。我会说,无论你觉得它是最干净,最具表现力和最自然的语法来做你正在尝试做的事情,并且不要为此感到愧疚。
如果您开始讨论文档,那么可能需要重新考虑您的职位。
答案 4 :(得分:5)
在这些情况下,记住优化的黄金规则非常重要:
你应该绝对不担心“滥用”linq,除非你明确地将其识别为性能问题的原因
答案 5 :(得分:5)
像任何东西一样,它可能被滥用。只要你远离明显糟糕的决定,如
var v = List.Where(...);
for(int i = 0; i < v.Count(); i++)
{...}
并了解执行的不同之处,然后它很可能不会比漫长的方式慢得多。根据Anders Hejlsburg(C#架构师)的说法,C#编译器在优化循环方面并不是特别擅长,但它在优化和并行化表达式树方面要好得多。及时,它可能比循环更有效。 List&lt;&gt;的ForEach版本实际上和for循环一样快,虽然我找不到证明这一点的链接。
P.S。我个人最喜欢的是ForEach&lt;&gt;不太知名的堂兄IndexedForEach(利用扩展方法)
List.IndexedForEach( (p,i) =>
{
if(i != 3)
p.DoSomething(i);
};
答案 6 :(得分:4)
LINQ可以像艺术一样。继续使用它来使代码美观。
答案 7 :(得分:1)
你正在回答你自己的问题,谈论为几毫秒的性能编写2-3倍的代码。我的意思是,如果您的问题域需要加速,那么是,如果不是可能不是。但是,它真的只有几毫秒的性能还是它&gt; 5%或> 10%。这是基于个案的价值判断。
答案 8 :(得分:0)
在哪里划线?
嗯,我们已经知道实现自己的quicksort in linq是个坏主意,至少与使用linq的orderby相比。
答案 9 :(得分:0)
我发现使用LINQ加快了我的开发速度,并且更容易避免循环可能引入的愚蠢错误。我曾经遇到LINQ的性能很差的情况,但那时我正在使用它来处理从具有数百万个节点的树结构中获取excel文件的数据。
答案 10 :(得分:0)
虽然我看到有一种观点认为LINQ可能会使声明更难以阅读,但我认为我的方法现在与他们正在解决的问题严格相关并且不花时间这一事实远远超过了它。包括查找循环或具有专用查找函数的混乱类。
花了一点时间习惯用LINQ做事,因为循环查找等已经成为这么长时间的主要选择了。我认为LINQ只是另一种类型的语法糖,可以以更优雅的方式完成相同的任务。现在,我仍然在处理繁重的任务关键代码时避免它 - 但这只是在LINQ发展之后性能提高。
答案 11 :(得分:0)
我对LINQ的唯一关注是它的连接实现。
正如我在尝试回答this question时所确定的那样(并且确认here),LINQ生成以执行联接的代码(必然,我猜)是天真的:对于列表中的每个项目, join通过联接列表执行线性搜索以查找匹配项。
向LINQ查询添加连接实质上将线性时间算法转换为二次时间算法。即使你认为过早的优化是所有邪恶的根源,从O(n)到O(n ^ 2)的跳跃应该让你停下来。 (如果您通过连接项目加入另一个集合,则为O(n ^ 3)。)
解决这个问题相对容易。例如,这个查询:
var list = from pr in parentTable.AsEnumerable()
join cr in childTable.AsEnumerable() on cr.Field<int>("ParentID") equals pr.Field<int>("ID")
where pr.Field<string>("Value") == "foo"
select cr;
类似于您在SQL Server中连接两个表的方式。但是它在LINQ中非常低效:对于where
子句返回的每个父行,查询将扫描整个子表。 (即使您正在加入未编制索引的字段,SQL Server也会构建一个哈希表来加速连接,如果可以的话。这有点超出了LINQ的工资等级。)
但是这个查询:
string fk = "FK_ChildTable_ParentTable";
var list = from cr in childTable.AsEnumerable()
where cr.GetParentRow(fk).Field<string>("Value") == "foo"
select cr;
产生相同的结果,但它只扫描子表一次。
如果您正在使用LINQ to objects,则同样的问题也适用:如果您想要连接任意大小的两个集合,您可能需要考虑实现一种更有效的方法来查找连接对象,例如:
Dictionary<Foo, Bar> map = buildMap(foos, bars);
var list = from Foo f in foos
where map[f].baz == "bat"
select f;