扩展方法语法与查询语法

67

我正在努力理解何时使用标准的LINQ关键字或带有Lambda表达式的LINQ扩展方法更好。它们似乎做着相同的事情,只是书写方式不同。这纯粹是风格问题吗?

var query = from p in Products
    where p.Name.Contains("foo")
    orderby c.Name
    select p;

// or with extension methods:
var query = Products
    .Where(p => p.Name.Contains("foo"))
    .OrderBy(p => p.Name);

第二个示例比较简洁,但如果你不知道 "=>" 的作用,可能表达力稍差,与第一个示例非常相似。

除了编写简洁的代码外,使用扩展方法与 LINQ 语法相比还有其他优点吗?


8个回答

36

老实说,有时候使用Funcs和Actions会受到情境的影响。比方说,如果你正在使用以下三个Func:

  Func<DataClasses.User, String> userName = user => user.UserName;
  Func<DataClasses.User, Boolean> userIDOverTen = user => user.UserID < 10;
  Func<DataClasses.User, Boolean> userIDUnderTen = user => user.UserID > 10;

正如您所见,第一个示例使用lambda表达式替换以获取用户名,第二个示例替换了用于检查ID是否小于10的lambda表达式,第三个示例现在应该很容易理解了。

注意:这只是一个愚蠢的例子,但它确实有效。

  var userList = 
    from user in userList
    where userIDOverTen(user)
    select userName;

对比

  var otherList =
    userList
    .Where(IDIsBelowNumber)
    .Select(userName)

在这个例子中,第二种方法较少冗长,因为扩展方法可以充分利用Func,但Linq表达式不能,因为它只查找布尔值而不是返回布尔值的Func。然而,在这种情况下,使用表达式语言可能更好。假设您已经有一个需要传入用户以外参数的方法:

  private Boolean IDIsBelowNumber(DataClasses.User user, 
          Int32 someNumber, Boolean doSomething)
  {
    return user.UserID < someNumber;
  }

注意:doSomething只是为了where扩展方法可以接受一个接受用户和整数并返回布尔值的方法而存在。对于这个例子来说有点恼人。

现在,如果您看一下Linq查询:

  var completeList =
     from user in userList
     where IDIsBelowNumber(user, 10, true)
     select userName;

你做得很好。现在是扩展方法:

  var otherList =
    userList
    .Where(IDIsBelowNumber????)
    .Select(userName)

如果没有 lambda 表达式,我实在无法调用那个方法。现在我需要做的是创建一个方法,它可以基于原始方法调用创建一个 Func。

   private Func<DataClasses.User, Boolean> IDIsBelowNumberFunc(Int32 number)
   {
      return user => IDIsBelowNumber(user, number, true);
   }

然后插入它:

  var otherList =
     userList
     .Where(IDIsBelowNumberFunc(10))
     .Select(userName)

所以你可以看到,有时候使用查询方法可能更容易一些。


私有的 Func<DataClasses.User, Boolean> IDIsBelowNumberFunc(Int32 number) { return user => IDIsBelowNumber(user, number, true); } 这里的 user 是在哪里定义的? - Munish Goyal
@MunishGoyal - 返回的是函数"user => IDIsBelowNumber(user, number, true)"。其中,"user"被定义为该函数的输入,返回值为"IDIsBelowNumber(user, number, true)"。 - AlexC

27

使用 LINQ 扩展方法(基于方法的查询)的一个优点是,你可以定义自定义的扩展方法而且它仍然很容易理解。

另一方面,当使用 LINQ 查询表达式 时,自定义扩展方法不在关键字列表中。与其他关键字混合使用会显得有些奇怪。

示例

我正在使用一个称为 Into 的自定义扩展方法,它只接受一个字符串:

带查询的示例

var query = (from p in Products
    where p.Name.Contains("foo")
    orderby c.Name
    select p).Into("MyTable");

扩展方法示例

var query = Products
                   .Where(p => p.Name.Contains("foo"))
                   .OrderBy(p => p.Name)
                   .Into("MyTable");

在我看来,当你有自定义扩展方法时,后一种方法,使用基于方法的查询方式(method-based query),阅读起来更加流畅。


只是一个小修正;在你的示例中,忘记在后面的LINQ扩展方法中添加一个闭合括号,靠近p.Name.Contains("foo") - chridam

15

我认为最好不要同时使用两种方法,应该选择其中一种并坚持使用。

这主要是基于个人喜好,但在查询语法(Comprehension method)中,并非所有的操作符都可用,如前所述。

我发现扩展方法语法更符合我的其余代码。我把 SQL 写在 SQL 中。只需使用扩展方法将每个内容添加到一起即可轻松构建表达式。

这只是我的意见。

由于我还不能发表评论,所以我想在 Programming Tool 的答案中发表一个评论:

为什么要为最后一个示例编写一个全新的方法?难道不能只使用:

.Where(user => IDIsBelowNumber(user, 10, true))


我相信你可以做到,但是我认为他试图创建一种简洁的方法,不需要传递用户。至少,我认为那就是发生的事情...那只是一个例子。 - Armstrongest
4
where IDIsBelowNumber(user, 10, true) 相比,.Where(user => IDIsBelowNumber(user, 10, true)) 有什么可怕之处? - Rodi

6

它们编译相同,且等效。就个人而言,我更喜欢使用Lambda(扩展)方法来完成大部分事情,只有在进行LINQ到SQL或试图模拟SQL时才使用语句(标准)。我发现Lambda方法与代码流程更加协调,而语句会在视觉上分散注意力。


如果它们编译成相同的输出,那么这些信息将得到+1分(一些支持网站链接将使答案迄今为止最好)。 - A. K-R

4

当我使用没有查询语法等效的Linq方法,例如FirstOrDefault()或其他类似方法时,我更喜欢使用扩展方法语法。


0

当:

  • 没有通过 Select 进行转换。
  • 没有中间变量。
  • 没有通过 SelectMany 进行笛卡尔积。

扩展方法语法更加简洁易读。查询语法有很多优点,但在结尾处写上 select p 是一个不好的选择。而且作为更大的方法链的一部分时,它也更加嘈杂。在下面的示例中,查询语法更加简洁:

var result =
  from a in tableA
  from b in tableB
  where a.SomeValue == b.SomeValue
  select Compose(a, b) as p
  orderby p.rank
  select p.ToArray();

-2

扩展方法/链式表达式的一个优点是提供了额外的操作符,例如Skip和Take。例如,如果您正在创建分页方法,能够跳过前10条记录并获取接下来的10条记录很容易实现。


-2

当我需要使用查询语法时,我喜欢使用它来进行查询,即一种惰性表达式,可以按需求进行评估。

像常规方法调用(方法语法或lambda语法)一样的方法看起来不够惰性,因此我将其作为一种约定。例如,

var query = from p in Products
            where p.Name.Contains("foo")
            orderby p.Name
            select p;

var result = query.ToList(); //extension method syntax

如果不是查询,我喜欢流畅的风格,这样看起来与其他急切执行的调用一致。
var nonQuery = Products.Where(p => p.Name.Contains("foo"))
                       .OrderBy(p => p.Name)
                       .ToList();

它帮助我更好地区分两种调用风格。当然,有时您会被迫使用方法语法,因此我的约定并不太具有说服力。


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