流畅查询和查询表达式 - 一个相对于另一个有什么好处吗?

265

LINQ是自泛型以来.NET中最重要的改进之一,它节省了我大量的时间和代码行数。 然而,与查询表达式语法相比,流畅的语法对我来说似乎更加自然。

var title = entries.Where(e => e.Approved)
    .OrderBy(e => e.Rating).Select(e => e.Title)
    .FirstOrDefault();

var query = (from e in entries
             where e.Approved
             orderby e.Rating
             select e.Title).FirstOrDefault();

这两者之间有什么区别,或者其中一个是否具有特定的优势?


1
对于复杂的查询,我发现lambda语法更易于理解/可读性更好,但查询语法只是更漂亮。 - nawfal
14个回答

267

两者并没有更好的一方:它们有不同的用途。当你想要利用多个范围变量时,查询语法就可以发挥其作用。这在以下三种情况下发生:

  • 使用 let 关键字时
  • 有多个生成器 (from 子句) 时
  • 进行连接操作时

这里是一个示例 (来自 LINQPad 样例):

string[] fullNames = { "Anne Williams", "John Fred Smith", "Sue Green" };

var query =
  from fullName in fullNames
  from name in fullName.Split()
  orderby fullName, name
  select name + " came from " + fullName;

现在将其与方法语法中的相同内容进行比较:

var query = fullNames
  .SelectMany (fName => fName.Split().Select (name => new { name, fName } ))
  .OrderBy (x => x.fName)
  .ThenBy  (x => x.name)
  .Select  (x => x.name + " came from " + x.fName);

相比之下,方法语法展示了全部的查询运算符并且在简单查询时更加简洁。通过混合查询语法和方法语法,你可以得到两种方式的最佳结合。这常常在LINQ to SQL查询中被使用:

var query =
  from c in db.Customers
  let totalSpend = c.Purchases.Sum (p => p.Price)    // Method syntax here
  where totalSpend > 1000
  from p in c.Purchases
  select new { p.Description, totalSpend, c.Address.State };

2
不错的答案。你能不能再多告诉我一点关于“.Select(name => new { name, fName })”是在做什么? - quillbreaker
12
它选择个人姓名中的单个单词(例如Anne、Williams、John等)以及完整姓名,并将它们存储在匿名类型中。这样你就可以“携带”原始的完整姓名,以便在查询的其余部分中访问完整姓名和单个单词。 - Joe Albahari

59

如果整个表达式都可以用后者(有时被称为“查询理解语法”)编写,我更喜欢使用后者。

var titlesQuery = from e in entries
                  where e.Approved
                  orderby e.Rating
                  select e.Titles;

var title = titlesQuery.FirstOrDefault();

一旦我需要添加圆括号和.MethodCalls(),我就会改变。

当我使用前者时,我通常将每个子句放在单独的一行,就像这样:

var title = entries
    .Where (e => e.Approved)
    .OrderBy (e => e.Rating)
    .Select (e => e.Title)
    .FirstOrDefault();

我发现那样更容易阅读。


29
每种风格都有其优缺点。在连接方面,查询语法更好,并且它有一个有用的let关键字,使得在查询中创建临时变量变得容易。
另一方面,流畅的语法有更多的方法和操作,这些方法和操作在查询语法中没有暴露出来。而且,由于它们只是扩展方法,你可以编写自己的方法。
我发现每次我开始使用查询语法编写LINQ语句时,我最终都不得不将其放在括号中,并退回到使用流畅的LINQ扩展方法。查询语法本身并没有足够的功能。

由于它们只是扩展方法,您可以编写自己的方法。-- 您会遇到这个问题吗?https://dev59.com/RG865IYBdhLWcg3wYNdS#3850254 - Nate Anderson

23

VB.NET 中,我非常喜欢使用查询语法。

我讨厌重复使用丑陋的 Function 关键字:

Dim fullNames = { "Anne Williams", "John Fred Smith", "Sue Green" };
Dim query =
     fullNames.SelectMany(Function(fName) fName.Split().
     Select(Function(Name) New With {Name, fName})).
     OrderBy(Function(x) x.fName).
     ThenBy(Function(x) x.Name).
     Select(Function(x) x.Name & " came from " & x.fName)

在我看来,这个简洁的查询更易读和易于维护:

query = From fullName In fullNames
        From name In fullName.Split()
        Order By fullName, name
        Select name & " came from " & fullName

VB.NET的查询语法比C#更强大且更简洁:https://dev59.com/UWw15IYBdhLWcg3wmM19#6515130

例如,这个操作DataSet(Objects)的LINQ查询:

VB.NET:

Dim first10Rows = From r In dataTable1 Take 10

C#:

var first10Rows = (from r in dataTable1.AsEnumerable() 
                   select r)
                   .Take(10);

12
我同情那些无法使用查询式的VB开发者。 - nawfal
1
你的最后一个 C# 示例过于简单化,缺少实际价值:你只是写了 dataTable1.AsEnumerable().Take(10); - Emyr
@Emyr:我上一个段落以“VB.NET的查询语法比C#更强大且不那么冗长”开始,只是在将VB.NET的查询语法与C#进行比较,而你正在使用方法语法。 - Tim Schmelter

18

我完全不理解查询语法。在我看来,它毫无意义。可以通过使用 .Select 和匿名类型来实现 let。我认为在其中加入 "标点符号" 后事情看起来更加有组织。


10
使用流畅的语法进行多次连接可能会变得非常繁琐。不过,除非涉及到连接,我通常会自己使用流畅的语法。 - Roman Starkov
1
@Instance Hunter:我也是。我花了很长时间才开始理解流畅语法的概念。现在,结合强大的可枚举性和“纯”函数的思想,我真的很喜欢它,以前棘手的情况没有了好的代码表示。对于那些古老的SQL部分,查询语法仍然是一种福音。 - Xan-Kun Clark-Davis

13

如果只有一个 where 子句,那么就使用流畅接口。 如果需要 select 或 orderby,我通常使用查询语法。


9

流畅的语法似乎更加强大,它也应该更适合将代码组织成小型可重用方法。


哪个是哪个? - user4951

5

我知道这个问题标记了C#,但是对于VB.NET来说,流畅语法过于冗长。


4

我非常喜欢Fluent语法,并尽可能地使用它,但在某些情况下,例如使用连接时,我通常更喜欢使用查询语法。在这些情况下,我发现查询语法更易于阅读,而且我认为有些人比较熟悉查询(类似SQL)语法,而不是lambda表达式。


4

虽然我理解和喜欢流畅的格式,但出于可读性的原因,我目前仍然坚持使用查询语法。刚接触LINQ的人会发现查询语法更容易理解。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接