我每天都在编程中经常使用LINQ。实际上,我很少(如果有的话)使用显式循环。但是,我发现我不再使用类似SQL的语法。我只是使用扩展功能。所以宁愿说:
from x in y select datatransform where filter
我用:
x.Where(c => filter).Select(c => datatransform)
您更喜欢哪种LINQ风格,团队中的其他人对此感到满意?
我每天都在编程中经常使用LINQ。实际上,我很少(如果有的话)使用显式循环。但是,我发现我不再使用类似SQL的语法。我只是使用扩展功能。所以宁愿说:
from x in y select datatransform where filter
我用:
x.Where(c => filter).Select(c => datatransform)
您更喜欢哪种LINQ风格,团队中的其他人对此感到满意?
Answers:
我发现不幸的是,Microsoft根据MSDN文档的立场是查询语法更可取,因为我从未使用过查询语法,而是一直使用LINQ方法语法。我喜欢能够激发我一心一意的查询。比较:
var products = from p in Products
where p.StockOnHand == 0
select p;
至:
var products = Products.Where(p => p.StockOnHand == 0);
更快,更少的线条,在我看来更干净。查询语法也不支持所有标准的LINQ运算符。我最近做过的一个示例查询看起来像这样:
var itemInfo = InventoryItems
.Where(r => r.ItemInfo is GeneralMerchInfo)
.Select(r => r.ItemInfo)
.Cast<GeneralMerchInfo>()
.FirstOrDefault(r => r.Xref == xref);
据我所知,使用查询语法(在可能的范围内)复制此查询将如下所示:
var itemInfo = (from r in InventoryItems
where r.ItemInfo is GeneralMerchInfo
select r.ItemInfo)
.Cast<GeneralMerchInfo>()
.FirstOrDefault(r => r.Xref == xref);
对我来说,它看起来不易读,并且您仍然需要知道如何使用方法语法。我个人真的很喜欢LINQ使得声明式样式成为可能,并在任何可能的情况下都使用它-有时对我不利。例如,使用方法语法,我可以执行以下操作:
// projects an InventoryItem collection with total stock on hand for each GSItem
inventoryItems = repository.GSItems
.Select(gsItem => new InventoryItem() {
GSItem = gsItem,
StockOnHand = repository.InventoryItems
.Where(inventoryItem => inventoryItem.GSItem.GSNumber == gsItem.GSNumber)
.Sum(r => r.StockOnHand)
});
我以为上面的代码对于没有良好文档的人来说很难理解,如果他们在LINQ中没有扎实的背景,他们可能还是不理解。尽管如此,方法语法仍具有一些强大的功能,可以快速(就代码行而言)设计查询以获取有关多个集合的聚合信息,否则这些集合将花费很多乏味的foreach循环。在这种情况下,方法语法对于从中获得的结果来说是非常紧凑的。尝试使用查询语法执行此操作可能很快就会变得笨拙。
我发现函数语法更令人赏心悦目。唯一的例外是,如果我需要加入两个以上的集合。Join()很快就会发疯。
添加另一个答案是否为时已晚?
我已经编写了大量的LINQ到对象的代码,并且我认为至少在该领域中,最好理解两种语法,以便使用任何使代码更简单的代码-并非总是点语法。
当然,有时候点句法是可行的方式-其他人提供了其中几种情况;但是,我认为理解能力是短暂的-如果您愿意,可以说唱不好。因此,我将提供一个样本,在该样本中我认为理解是有用的。
这是解决数字替换难题的解决方案:(使用LINQPad编写的解决方案,但可以在控制台应用程序中独立运行)
// NO
// NO
// NO
//+NO
//===
// OK
var solutions =
from O in Enumerable.Range(1, 8) // 1-9
//.AsQueryable()
from N in Enumerable.Range(1, 8) // 1-9
where O != N
let NO = 10 * N + O
let product = 4 * NO
where product < 100
let K = product % 10
where K != O && K != N && product / 10 == O
select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
//Console.WriteLine("\nsolution expression tree\n" + solutions.Expression);
...输出:
N = 1,O = 6,K = 4
还不错,逻辑线性地流动,我们可以看到它提供了一个正确的解决方案。这个难题很容易手动解决:推理3>> N
0,并且O
> 4 * N意味着8> = O
> =4。这意味着最多要手工测试10个案例(2为N
-by- 5为O
)。我已经迷路了-这个谜题仅供LINQ插图之用。
编译器做了很多工作,将其转换为等效的点语法。除了通常的第二和后续from
子句变成SelectMany
调用外,我们还有一些let
子句变成Select
带有投影的调用,这两个子句都使用了透明标识符。正如我将要展示的那样,必须在点语法中命名这些标识符,这会使该方法的可读性大大降低。
我有一个技巧可以揭露编译器将这段代码转换为点语法的作用。如果取消注释上面的两条注释行并再次运行它,将得到以下输出:
N = 1,O = 6,K = 4
解决方案表达式树System.Linq.Enumerable + d_ b8.SelectMany(O => Range(1,8),(O,N)=> new <> f _AnonymousType0
2(O = O, N = N)).Where(<>h__TransparentIdentifier0 => (<>h__TransparentIdentifier0.O != <>h__TransparentIdentifier0.N)).Select(<>h__TransparentIdentifier0 => new <>f__AnonymousType1
2(<> h_ TransparentIdentifier0 = <> h _TransparentIdentifier0,NO = ((10 * <> H_ TransparentIdentifier0.N)+ <>ħ _TransparentIdentifier0.O)))。选择(<> H_ TransparentIdentifier1 =>新<>˚F _AnonymousType22(<>h__TransparentIdentifier1 = <>h__TransparentIdentifier1, product = (4 * <>h__TransparentIdentifier1.NO))).Where(<>h__TransparentIdentifier2 => (<>h__TransparentIdentifier2.product < 100)).Select(<>h__TransparentIdentifier2 => new <>f__AnonymousType3
2(<> H_ TransparentIdentifier2 = <>ħ _TransparentIdentifier2,K =( <> h_ TransparentIdentifier2.product%10)))。Where(<> h _TransparentIdentifier3 =>((((<> h_ TransparentIdentifier3.K!= <> h _TransparentIdentifier3。<> h_ TransparentIdentifier2。<>h _TransparentIdentifier1。<> h_TransparentIdentifier0.O)AndAlso(<> h _TransparentIdentifier3.K!= <> h_ TransparentIdentifier3。<> h _TransparentIdentifier2。<> h_ TransparentIdentifier1。<> h _TransparentIdentifier0.N))AndAlso((<> h_ TransparentIdentifier3。产物/ 10)== <> H_ TransparentIdentifier3。<>ħ _TransparentIdentifier2。<> H_ TransparentIdentifier1。<>ħ _TransparentIdentifier0.O)))。选择(<> H_ TransparentIdentifier3 =>新<>˚F _AnonymousType4`3(N = < > h_ 透明标识符3 。<> h _TransparentIdentifier2。<> h_ 透明标识符1 。<> h _TransparentIdentifier0.N,O = <> h_ TransparentIdentifier3。<> h_TransparentIdentifier2。<> h_ TransparentIdentifier1。<> h _TransparentIdentifier0.O,K = <> h__TransparentIdentifier3.K))
将每个LINQ运算符放在新的一行上,将“无法说”的标识符转换为我们可以“说”的标识符,将匿名类型更改为它们熟悉的形式,并更改AndAlso
表达式树的术语以&&
暴露编译器所做的转换,以达到等效的目的用点语法:
var solutions =
Enumerable.Range(1,8) // from O in Enumerable.Range(1,8)
.SelectMany(O => Enumerable.Range(1, 8), (O, N) => new { O = O, N = N }) // from N in Enumerable.Range(1,8)
.Where(temp0 => temp0.O != temp0.N) // where O != N
.Select(temp0 => new { temp0 = temp0, NO = 10 * temp0.N + temp0.O }) // let NO = 10 * N + O
.Select(temp1 => new { temp1 = temp1, product = 4 * temp1.NO }) // let product = 4 * NO
.Where(temp2 => temp2.product < 100) // where product < 100
.Select(temp2 => new { temp2 = temp2, K = temp2.product % 10 }) // let K = product % 10
.Where(temp3 => temp3.K != temp3.temp2.temp1.temp0.O && temp3.K != temp3.temp2.temp1.temp0.N && temp3.temp2.product / 10 == temp3.temp2.temp1.temp0.O)
// where K != O && K != N && product / 10 == O
.Select(temp3 => new { N = temp3.temp2.temp1.temp0.N, O = temp3.temp2.temp1.temp0.O, K = temp3.K });
// select new { N, O, K };
foreach(var i in solutions)
{
Console.WriteLine("N = {0}, O = {1}, K = {2}", i.N, i.O, i.K);
}
如果运行,可以验证再次输出:
N = 1,O = 6,K = 4
我想答案是NONBHN(不仅是,但还是地狱!)-因为它太复杂了。当然,您可以想出一些比“ temp0” ..“ temp3”更有意义的标识符名称,但要点是,它们不会在代码中添加任何内容-它们不会使代码性能更好,它们不会为了使代码读起来更好,他们只会弄糟代码,而如果您是手工做的话,那么毫无疑问,您会花一两个时间弄乱它,然后再进行修改。另外,玩“名称游戏”对于找到有意义的标识符已经足够困难了,因此,我欢迎编译器在查询理解方面为我提供的名称游戏突破。
这个难题样品可能不是真实世界足以让你认真对待; 但是,在查询理解方面确实存在其他场景:
Join
and 的复杂性GroupJoin
:查询理解join
子句中范围变量的作用域将原本可能会以点语法编译的错误转换为理解语法中的编译时错误。from
子句,join
&join..into
子句和let
子句。我知道在我的家乡有不止一家工程师店,这些店禁止理解语法。我认为这很遗憾,因为理解语法只是一种工具和有用的工具。我认为这很像是说:“有些事情您可以用螺丝刀来做,而凿子则不能。因为您可以使用螺丝刀作为凿子,所以从国王的法令开始,凿子就被禁止了。”
我的建议是,当可以使用理解语法完成整个表达式时,请使用查询理解语法。也就是说,我希望:
var query = from c in customers orderby c.Name select c.Address;
至
var query = customers.OrderBy(c=>c.Name).Select(c=>c.Address);
但我宁愿
int count = customers.Where(c=>c.City == "London").Count();
至
int count = (from c in customers where c.City == "London" select c).Count();
我希望我们提出了一些语法,可以更好地将两者混合使用。就像是:
int count = from c in customers
where c.City == "London"
select c
continue with Count();
但是可惜我们没有。
但基本上,这是一个偏好问题。做一个对您和您的同事来说更好的选择。
var londonCustomers = from c in ...; int count = londonCustomers.Count();
我倾向于使用非查询语法,除非我需要在查询中途定义一个变量,例如
from x in list
let y = x.DoExpensiveCalulation()
where y > 42
select y
但是我写非查询语法像
x.Where(c => filter)
.Select(c => datatransform)