LINQ详解

LINQ详解

1. 查询语法和方法语法

LINQ 提供了两种查询方式:1.查询语法:使用类似 SQL 的语句结构。2.方法语法:使用一系列扩展方法链式调用来构建查询。

2. 基本查询操作

基本用法Select方法通常被调用在一个可枚举对象上,它接受一个匿名函数作为参数,这个函数定义了如何转换集合中的每个元素。

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var squaredNumbers = numbers.Select(n => n * n).ToList();

Select接收一个lambda表达式n => n * n,它将列表中的每个数字平方。 ToList()方法则将结果转换为一个新的List。

更复杂的用法Select也可以用于创建更复杂的数据结构,比如从一组对象中提取特定属性,或者创建新的匿名类型:

var customers = new List<Customer>
{
    new Customer { Name = "Alice", Age = 30 },
    new Customer { Name = "Bob", Age = 25 }
};
var customerNames = customers.Select(c => c.Name).ToList();
var nameAgePairs = customers.Select(c => new { Name = c.Name, Age = c.Age }).ToList(); // 或者创建一个匿名类型

注意事项Select操作是延迟执行的,意味着直到你迭代结果或调用如ToList(),ToArray(), First(), Count()等终结操作时,转换才实际发生。

Where: 过滤元素。

Where用于根据指定的条件筛选数据集合。保留那些符合条件的元素。Where 方法通常与 Select 结合使用,以实现数据的筛选和转换。

基本用法

var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();

Where 方法接收一个 lambda 表达式 n => n % 2 == 0,它检查每个数字是否为偶数。结果 evenNumbers 将只包含原列表中的偶数。

复杂用法Where 方法可以用于更复杂的条件判断,例如在对象集合中筛选:

public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
}
var students = new List<Student>
{
    new Student { Name = "Alice", Age = 20 },
    new Student { Name = "Bob", Age = 22 },
    new Student { Name = "Charlie", Age = 19 }
};
var adultStudents = students.Where(s => s.Age >= 21).ToList();

在这个例子中,Where 方法筛选出年龄大于等于 21 岁的学生。

OrderBy / OrderByDescending: 对元素排序。
OrderBy

OrderBy 方法按照指定的键对集合进行升序排序。它接受一个 lambda 表达式作为参数,该表达式定义了用于排序的键。

基本用法

 var numbers = new List<int> { 5, 3, 7, 1, 2 };
 var sortedNumbers = numbers.OrderBy(n => n).ToList(); // 现在是 { 1, 2, 3, 5, 7 }

对象排序对于对象集合,你可以选择对象的某个属性作为排序键:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
var people = new List<Person>
{
    new Person { Name = "Charlie", Age = 30 },
    new Person { Name = "Alice", Age = 25 },
    new Person { Name = "Bob", Age = 35 }
};
var sortedPeople = people.OrderBy(p => p.Age).ToList();//将按年龄从小到大排序
OrderByDescending

OrderByDescending 方法与 OrderBy 类似,但是它按照指定的键进行降序排序。

基本用法

 var sortedNumbers = numbers.OrderByDescending(n => n).ToList(); //现在是 { 7, 5, 3, 2, 1 }

对象排序同样,对于对象集合,你可以选择对象的某个属性作为排序键:

var sortedPeople = people.OrderByDescending(p => p.Age).ToList();//将按年龄从大到小排序

链接多个排序规则

你可以链接多个 OrderBy 或 ThenBy(以及它们的降序版本)来实现多列排序:

var people = new List<Person>
{
    new Person { Name = "Charlie", Age = 30 },
    new Person { Name = "Alice", Age = 25 },
    new Person { Name = "Bob", Age = 30 }
};
var sortedPeople = people.OrderBy(p => p.Age).ThenBy(p => p.Name).ToList();

在这个例子中,首先按年龄排序,然后在相同年龄的人中按名字排序。

GroupBy: 将元素分组。

GroupBy 可以让你基于一个或多个属性或计算结果将数据分类,从而实现更高级的数据分析和处理。基本用法GroupBy 方法接受一个函数作为参数,这个函数用于确定每个元素所属的组。函数的输出通常是元素的一个属性或基于元素计算得到的值。例如

var fruits = new List<string> { "Apple", "Banana", "Cherry", "Apricot", "Blueberry" };
var groupedFruits = fruits.GroupBy(f => f[0]).ToList();
foreach (var group in groupedFruits)
{
    Console.WriteLine($"Fruits starting with {group.Key}:");
    foreach (var fruit in group)
    {
        Console.WriteLine(fruit);
    }
}

在这个例子中,GroupBy 根据水果名称的第一个字母进行分组。

使用匿名类型进行分组你还可以使用匿名类型来定义更复杂的分组键:

public class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Department { get; set; }
}
var employees = new List<Employee>
{
    new Employee { Name = "Alice", Age = 25, Department = "HR" },
    new Employee { Name = "Bob", Age = 30, Department = "IT" },
    new Employee { Name = "Charlie", Age = 25, Department = "HR" }
};
var groupedEmployees = employees.GroupBy(e => new { e.Age, e.Department }); 
foreach (var group in groupedEmployees)
{
    Console.WriteLine($"Age: {group.Key.Age}, Department: {group.Key.Department}");
    foreach (var employee in group)
    {
        Console.WriteLine(employee.Name);
    }
}

在这个例子中,员工们被按照年龄和部门进行分组。

Join: 连接多个序列。

Join 类似于 SQL 中的 JOIN 操作。它允许你基于两个序列中元素的共同属性或键来组合数据,产生一个新的序列,其中包含了来自两个序列的匹配项。

基本用法

1. from outer in outerSequence
2. join inner in innerSequence on outerKeySelector equals innerKeySelector
3. select new { Outer = outer, Inner = inner };

或者使用方法语法:

outerSequence.Join(
    innerSequence,
    outer => outerKeySelector(outer),
    inner => innerKeySelector(inner),
    (outer, inner) => new { Outer = outer, Inner = inner });

这里,outerSequence 和 innerSequence 是要连接的两个序列;outerKeySelector 和 innerKeySelector 是用于选择每个序列中元素的键的函数;最后的 lambda 表达式定义了如何组合匹配的元素。

示例假设我们有两个集合,一个是 Customers,另一个是 Orders,我们想要找出每个客户的所有订单:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}
public class Order
{
    public int Id { get; set; }
    public int CustomerId { get; set; }
}
var customers = new List<Customer>
{
    new Customer { Id = 1, Name = "Alice" },
    new Customer { Id = 2, Name = "Bob" }
};
var orders = new List<Order>
{
    new Order { Id = 101, CustomerId = 1 },
    new Order { Id = 102, CustomerId = 2 },
    new Order { Id = 103, CustomerId = 1 }
};
var joinedData = from c in customers
    join o in orders on c.Id equals o.CustomerId
    select new { Customer = c, Order = o };
foreach (var item in joinedData)
{
    Console.WriteLine($"Customer: {item.Customer.Name}, Order ID: {item.Order.Id}");
}

或者使用方法语法:

var joinedData = customers.Join(
orders,
c => c.Id,
o => o.CustomerId,
(c, o) => new { Customer = c, Order = o })

处理多个匹配项

当一个外键在内序列中有多个匹配项时,Join 返回的将是包含多个内序列元素的 IEnumerable。为了处理这种情况,你可以使用 GroupJoin 方法,或者在 Join后面加上 SelectMany 来扁平化结果。

Any

Any 方法用于检查集合中是否存在至少一个元素满足指定的条件。如果没有提供条件,则 Any会检查集合是否为空。

用法示例

var numbers = new List<int> { 1, 2, 3, 4, 5 };
bool containsEven = numbers.Any(n => n % 2 == 0); // true
bool isNotEmpty = numbers.Any(); // true
All

All 方法用于检查集合中所有元素是否都满足指定的条件。如果集合为空,All 默认返回 true,因为没有元素违反条件。

用法示例

var numbers = new List<int> { 2, 4, 6, 8 };
bool allEven = numbers.All(n => n % 2 == 0); // true
bool allPositive = numbers.All(n => n > 0); // true
Count / Sum / Average / Min / Max: 计算聚合值。

Count, Sum, Average, Min, 和 Max 是 LINQ 中用于计算集合中元素的聚合值的一系列方法。这些方法提供了快速且有效的方式来进行数据汇总,是数据分析和统计计算的基础。

Count

示例计算集合中满足特定条件的元素数量。如果没有提供条件,则计算集合中所有元素的数量。

var numbers = new List<int> { 1, 2, 3, 4, 5 };
int evenCount = numbers.Count(n => n % 2 == 0); // 2
int totalCount = numbers.Count(); // 5
Sum

示例计算集合中所有元素的总和,或者满足特定条件的元素的总和。

var numbers = new List<int> { 1, 2, 3, 4, 5 };
int totalSum = numbers.Sum(); // 15
int evenSum = numbers.Where(n => n % 2 == 0).Sum(); // 6
Average

示例计算集合中所有元素的平均值,或者满足特定条件的元素的平均值。

var numbers = new List<double> { 1.0, 2.0, 3.0, 4.0, 5.0 };
double average = numbers.Average(); // 3.0
double evenAverage = numbers.Where(n => n % 2 == 0).Average(); // 3.0
Min 和 Max

示例分别用于查找集合中最小和最大的元素,或者满足特定条件的最小和最大元素。

var numbers = new List<int> { 1, 2, 3, 4, 5 };
int min = numbers.Min(); // 1
int max = numbers.Max(); // 5
int evenMin = numbers.Where(n => n % 2 == 0).Min(); // 2
int evenMax = numbers.Where(n => n % 2 == 0).Max(); // 

3. 常用的 LINQ 方法

FirstOrDefault

功能 FirstOrDefault 方法返回集合中的第一个元素,或者满足给定条件的第一个元素(如果提供了条件)。如果集合为空或没有元素满足条件,则返回默认值(对于引用类型来说通常是 null)。

语法

 var firstItem = collection.FirstOrDefault();
 var firstItemByCondition = collection.FirstOrDefault(item => item.Property == value);
LastOrDefault

功能LastOrDefault 方法与 FirstOrDefault 类似,但是它返回的是集合中的最后一个元素,或者满足给定条件的最后一个元素。如果集合为空或没有元素满足条件,同样返回默认值。

语法

 var lastItem = collection.LastOrDefault();
 var lastItemByCondition = collection.LastOrDefault(item => item.Property == value);

示例假设我们有一个 List 集合:

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
// 获取第一个元素
int? firstNumber = numbers.FirstOrDefault(); 
// 获取第一个偶数元素,如果没有则返回 null
int? firstEvenNumber = numbers.FirstOrDefault(n => n % 2 == 0); 
// 获取最后一个元素
int? lastNumber = numbers.LastOrDefault();
// 获取最后一个偶数元素,如果没有则返回 null
int? lastEvenNumber = numbers.LastOrDefault(n => n % 2 == 0); 
SingleOrDefault: 返回唯一元素或默认值。

SingleOrDefault 是 LINQ 中的一个扩展方法,用于从序列中获取唯一匹配指定条件的元素,或者当序列为空时返回默认值。这个方法结合了 First 和 FirstOrDefault 的特性,但增加了对结果唯一性的检查。如果找到一个以上的元素满足条件,SingleOrDefault 将抛出 InvalidOperationException异常。

语法

var singleItem = collection.SingleOrDefault();
var singleItemByCondition = collection.SingleOrDefault(item => item.Property == value);

使用场景当你期望序列中有零个或一个元素满足条件时,避免在序列中有多个元素满足条件的情况,从而确保数据的一致性和正确性。

示例假设我们有一个 List 集合,其中 Person 类有 Name 和 Age 属性:

List<Person> people = new List<Person>
{
    new Person { Name = "Alice", Age = 30 },
    new Person { Name = "Bob", Age = 25 }
};// 获取年龄为 30 的人,如果没有则返回 null
Person personWithAge30 = people.SingleOrDefault(p => p.Age == 30); 
// 尝试获取年龄为 25 和 30 的人,这将抛出异常
try
{
    Person multipleAges = people.SingleOrDefault(p => p.Age == 25 || p.Age == 30);
}
catch (InvalidOperationException ex)
{
    Console.WriteLine("Multiple items found: " + ex.Message);
}
ElementAt / ElementAtOrDefault: 返回指定索引处的元素。

**Concat: 合并两个序列。**将两个或多个序列合并成一个新的序列。这对于需要将多个数据源组合在一起进行处理的场景非常有用。

语法

IEnumerable<T> concatenatedSequence = sequence1.Concat(sequence2);
IEnumerable<T> concatenatedSequence = sequence1.Concat(sequence2).Concat(sequence3); 

示例假设我们有两个 List 集合:

List<int> list1 = new List<int> { 1, 2, 3 };
List<int> list2 = new List<int> { 4, 5, 6 };
IEnumerable<int> combinedList = list1.Concat(list2); // 使用 Concat 合并两个列表
foreach (int number in combinedList)
{
    Console.WriteLine(number);// 输出: 1, 2, 3, 4, 5, 6
} 
Distinct: 移除重复元素。

[扩展方法],用于从序列中删除重复的元素,只保留唯一的元素。这对于数据清洗、去重以及确保数据集中的元素唯一性非常有用。

语法

IEnumerable<T> distinctSequence = source.Distinct();

示例假设我们有一个包含重复元素的 List 集合:

List<int> numbers = new List<int> { 1, 2, 2, 3, 4, 4, 4, 5 };
IEnumerable<int> uniqueNumbers = numbers.Distinct(); // 使用 Distinct 去除重复元素
foreach (int number in uniqueNumbers)
{
    Console.WriteLine(number); // 输出: 1, 2, 3, 4, 5
}

对于自定义类型,例如 Person 类,如果需要基于某个属性(如 Name)去重,可以这样做:

List<Person> people = new List<Person>
{
    new Person { Name = "Alice" },
    new Person { Name = "Bob" },
    new Person { Name = "Alice" }
};
// 使用自定义比较器去重
var comparer = EqualityComparer<Person>.Default;
var uniquePeople = people.Distinct(comparer); 
// 或者使用匿名函数
var uniquePeopleByNames = people.Distinct(person => person.Name);
Reverse: 反转序列。
Skip / Take: 跳过或获取序列的一部分。
ToArray / ToList: 将序列转换为数组或列表。

(作为个人笔记学习使用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值