1. 入门
在LINQ的世界里,其实有两大“流派”写查询,两种风格,乍一看好像完全不一样。就是Query Syntax(查询语法)和Method Syntax(方法语法)。如果你一直觉得编程就是死板的逻辑,那今天我们要加点艺术感,因为选语法风格有点像选画笔还是铅笔:都能画画,但一个更适合上色,一个更适合勾线。
为啥要有两种语法?好问题!想象下你点咖啡。你可以说:“麻烦来一杯加牛奶和一勺糖的意式浓缩?”。也可以写在纸上:“意式浓缩。牛奶。1糖”。两种都能懂,但一个更口语,一个更简洁。
LINQ也是这样。一种语法就是为了让你写起来像SQL数据库那样,另一种更“C#风格”也更灵活。其实C#编译器最后都会把这两种写法转成一样的:方法调用。所以选哪个,主要看你觉得哪个更好读、自己喜欢哪个。
我们来看看这两种写法。
2. Method Syntax
这种写法就是你把LINQ的扩展方法一个个点出来连着用。这种方式在.NET开发者里特别流行,因为很容易加新操作,结果总是IEnumerable<T>,而且直接在代码里写很顺手。
例子:筛选并排序商品
var filteredProducts = products
.Where(p => p.Price < 1000) // 按价格筛选
.OrderBy(p => p.Name) // 按名字排序
.ToList(); // 转成 List<Product>
Where、OrderBy 和 ToList 都是LINQ的扩展方法,每个都会返回一个新的数据集,可以继续链式处理。
流程图
graph LR
A[products] --> B[Where]
B --> C[OrderBy]
C --> D[ToList]
Method Syntax的优点
- 超灵活:可以很容易地拼出很复杂的操作链。
- 所有方法都是C#方法,IDE会自动补全。
- 几乎LINQ能做的事都能做(甚至还能更多)。
再举个例子:选出所有18岁以上用户的名字
假设我们有个User类:
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
现在选出成年用户的名字:
List<User> users = ... // 某处声明的
var adultNames = users
.Where(u => u.Age >= 18)
.Select(u => u.Name)
.ToList();
3. Query Syntax(SQL风格语法)
这种语法就是专门让C#开发者有种写SQL的感觉。如果你以前写过SQL数据库,这种写法会很亲切。它以from关键字开头。
例子:同样的筛选和排序
var filteredProducts =
from p in products
where p.Price < 1000
orderby p.Name
select p;
这就像经典的SQL查询:“从products里拿p,p.Price < 1000,按p.Name排序,选p”。
流程图
flowchart TD
A[products] -->|from p in products| B[where p.Price < 1000]
B --> C[orderby p.Name]
C --> D[select p]
Query Syntax的优点
- 像SQL,数据库老手一看就懂。
- 写多条件、分组、连接(join)时更清晰。
再举个例子:选出成年用户的名字
var adultNames =
from u in users
where u.Age >= 18
select u.Name;
注意:这里select后面可以直接选字段,比如只要名字,不用整个对象。
4. 对比:Method Syntax vs Query Syntax
| Method Syntax | Query Syntax | |
|---|---|---|
| 语法 | |
|
| 像什么 | 普通方法/链式调用 | SQL |
| 适用场景 | 任何操作都能用 | 不是所有操作都能用(比如、只能Method Syntax) |
| 可读性 | 链式操作很清楚 | 分组、连接时更方便 |
| 返回什么 | 通常是 |
通常是,但有时要变成列表 |
现实生活中的有趣事实
微软官方LINQ文档里的例子,基本都是两种写法都给你。但实际项目里,Method Syntax用得更多——因为链扩展方法(Where、Select、OrderBy等)更顺手。
混用还是不混用?怎么选风格
可以在一个项目里混着用两种语法(甚至一个查询里都能混,不过那样看着怪怪的)。最重要的是别把代码写成大杂烩。一般一个模块或项目选一种风格,这样代码更好读。
Method Syntax特别适合连续转换和用所有LINQ方法(有些方法只有方法写法,比如Sum、Count、Any等)。
Query Syntax在有join、group by或多层复杂条件时,读起来更舒服。
5. 风格转换:其实本质都一样
C#里的LINQ会把所有SQL风格的查询(Query Syntax)转成方法链(Method Syntax)。也就是说,不管你怎么写,C#编译时都会变成扩展方法。
例1——筛选:
Query Syntax:
var adults = from u in users
where u.Age >= 18
select u;
Method Syntax(等价):
var adults = users.Where(u => u.Age >= 18);
例2——字段选择(Select):
Query Syntax:
var names = from u in users
select u.Name;
Method Syntax:
var names = users.Select(u => u.Name);
7. 分组和连接(join、group by)
创建新对象
我们给学习项目加个功能,显示按年龄分组的用户列表。这时候两种语法的差别就很明显了。
Query Syntax(分组):
var usersByAge =
from u in users
group u by u.Age into ageGroup
select new { Age = ageGroup.Key, Users = ageGroup.ToList() };
我们按年龄分组用户(group u by u.Age)。into后面出现的ageGroup就是分组对象。
Method Syntax(等价):
var usersByAge = users
.GroupBy(u => u.Age)
.Select(ageGroup => new { Age = ageGroup.Key, Users = ageGroup.ToList() });
再复杂点:连接(join)
假设我们有订单列表(orders)和用户列表(users)。我们想得到用户名字和他们订单的总额。
Query Syntax:
var userOrders =
from user in users
join order in orders on user.Id equals order.UserId
select new { user.Name, order.Amount };
Method Syntax:
var userOrders = users.Join(
orders,
user => user.Id,
order => order.UserId,
(user, order) => new { user.Name, order.Amount }
);
8. 常见错误、坑和小技巧
最常见的误区之一:如果你没加.ToList(),结果其实不是列表,而是“延迟查询”(lazy query),只有你第一次遍历时才会真正执行。这很方便,但如果你写完查询后原集合变了,可能会有意外结果。关于“延迟执行”我们后面还会专门聊(等你熟悉LINQ方法后)。
很多新手会搞混,不是所有LINQ方法都能在Query Syntax里用。比如不能直接写select sum(u.Age),得用Method Syntax(users.Sum(u => u.Age))。
GO TO FULL VERSION