文章目录
- `Select`
- `SelectMany`
- `Take`
- `TakeWhile`
- `Take`
- `SkipWhile`
- `Join`
- `GroupJoin`
- `OrderBy`
- `OrderByDescending`
- `ThenBy`
Select
当谈到 C# LINQ 的 Select 方法时,它是一个非常强大的工具,可以用于各种不同的用途。下面是一些常见的用法:
- 投影(Projection)
Select 最常见的用途是对集合中的每个元素执行转换操作,从而创建一个新的序列。这可以用来选择特定的属性、计算派生值或执行任何自定义的转换逻辑。
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
List<string> strings = numbers.Select(n => "Number " + n.ToString()).ToList();
在上述示例中,我们使用 Select 将整数列表 numbers 转换为字符串列表 strings,其中每个元素都以 "Number " 开头。
- 投影到匿名类型(Projection to Anonymous Type)
Select 可以用于将元素投影到匿名类型,方便地创建一个只包含所需属性的新对象。
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 },
new Person { Name = "Charlie", Age = 40 }
};
var anonymousObjects = people.Select(p => new { FirstName = p.Name, AgeCategory = p.Age <= 30 ? "Young" : "Old" }).ToList();
在上述示例中,我们有一个包含人员信息的人员列表 people。使用 Select,我们将每个人员投影到一个匿名类型中,并只选择 Name 和 AgeCategory 属性。
- 字符串连接(String Concatenation)
使用 Select 方法和字符串插值(string interpolation),我们可以将集合中的元素连接为单个字符串。
List<string> animals = new List<string> { "cat", "dog", "bird" };
string concatenatedString = string.Join(", ", animals.Select(a => $"I love {a}"));
在上述示例中,我们使用 Select 将集合中的每个动物名称转换为一个带有字符串插值的句子,并使用 string.Join 将这些句子连接为一个字符串。
- 聚合(Aggregation)
Select 还可以与其他聚合函数(例如 Sum、Max、Min、Average 等)结合使用,以对集合中的元素进行计算汇总。
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
int sumOfSquares = numbers.Select(n => n * n).Sum();
int maxNumber = numbers.Select(n => n * 2).Max();
在上述示例中,我们使用 Select 将每个数字平方,并使用 Sum 和 Max 方法分别计算平方值的总和和最大值。
SelectMany
SelectMany 是 LINQ 中的一个方法,用于将嵌套的集合(集合的集合)转换为单个扁平的序列。它可以将嵌套结构的序列展平为单个序列,使操作更便捷。
以下是 SelectMany 方法的语法:
public static IEnumerable<TResult> SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector);
SelectMany 方法接受一个源序列以及一个返回子序列的函数,并将每个元素的子序列连接起来。它返回一个实现了 IEnumerable 接口的延迟执行的查询结果序列。
这是一个使用 SelectMany 方法的示例代码:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<List<int>> nestedNumbers = new List<List<int>>
{
new List<int> { 1, 2, 3 },
new List<int> { 4, 5, 6 },
new List<int> { 7, 8, 9 }
};
// 使用 SelectMany 方法将所有子序列连接成一个平坦的序列
IEnumerable<int> flattenedNumbers = nestedNumbers.SelectMany(x => x);
// 遍历平坦的序列
foreach (int number in flattenedNumbers)
{
Console.WriteLine(number);
}
}
}
在上述示例中,我们创建了一个嵌套的整数列表 nestedNumbers,其中包含三个子列表。然后,我们使用 SelectMany 方法将这些子列表连接成一个平坦的整数序列。
Take
在 C# 的 LINQ 中,Take 是用于从集合中获取指定数量的元素的方法。它可以用来限制查询结果的大小或获取集合中的前几个元素。
Take 方法的语法如下:
IEnumerable<T> Take<T>(this IEnumerable<T> source, int count)
其中,source 是要操作的集合本身,count 是要获取的元素数量。
下面是 Take 方法的几个用法示例:
- 获取集合的前几个元素
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<int> firstThreeNumbers = numbers.Take(3);
在上述示例中,我们使用 Take 方法获取了 numbers 集合的前三个元素(即 1、2 和 3)。
- 限制查询结果的大小
List<string> names = new List<string> { "Alice", "Bob", "Charlie", "David", "Eve" };
IEnumerable<string> limitedNames = names.Take(2);
在上述示例中,我们使用 Take 方法限制了查询结果最多只能包含前两个元素(即 “Alice” 和 “Bob”)。
- 与其他 LINQ 方法组合使用
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<int> evenNumbers = numbers.Where(n => n % 2 == 0).Take(2);
在上述示例中,我们先使用 Where 方法筛选出偶数,然后再使用 Take 方法获取前两个偶数。
需要注意的是,Take 方法不会修改原始集合,它只返回一个包含指定数量元素的新的可枚举序列。
TakeWhile
在 TakeWhile 方法中,它会根据指定的条件逐个获取元素,直到遇到第一个不满足条件的元素为止。
以下是正确的示例:
List<int> numbers = new List<int> { 2, 4, 6, 1, 8, 10 };
IEnumerable<int> consecutiveEvenNumbers = numbers.TakeWhile(n => n % 2 == 0);
在此示例中,使用 TakeWhile 方法获取 numbers 列表中连续的偶数元素。返回的结果是 2、4 和 6,因为在第一个奇数 1 出现后,就停止获取。
Take
在LINQ中,Skip方法用于跳过序列中指定数量的元素,并返回剩余的元素。它允许你忽略集合的前几个元素,从指定位置开始获取后续的元素。
Skip方法的语法如下:
IEnumerable<T> Skip<T>(this IEnumerable<T> source, int count)
其中,source是要操作的集合本身,count是要跳过的元素数量。
跳过指定数量的元素:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<int> remainingNumbers = numbers.Skip(2);
在上述示例中,我们使用Skip方法跳过了集合numbers中的前两个元素。返回的结果是3、4、5。
SkipWhile
在LINQ中,SkipWhile方法用于跳过序列中满足指定条件的连续元素,并返回剩余的元素。它允许你跳过集合中一系列符合条件的元素,直到遇到不满足条件的元素为止。
SkipWhile方法的语法如下:
IEnumerable<T> SkipWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate)
其中,source是要操作的集合本身,predicate是一个用于确定是否要跳过当前元素的函数。
以下是SkipWhile方法的几个用法示例:
- 跳过满足条件的连续元素:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
IEnumerable<int> remainingNumbers = numbers.SkipWhile(n => n < 3);
在上述示例中,我们使用SkipWhile方法跳过集合numbers中满足条件n < 3的连续元素。返回的结果是3、4、5,因为在遇到第一个不满足条件的元素3后,停止跳过。
- 结合索引使用:
List<int> numbers = new List<int> { 1, 2, 5, 3, 4 };
IEnumerable<int> remainingNumbers = numbers.SkipWhile((n, index) => n < index);
在上述示例中,我们在SkipWhile方法的条件函数中使用了元素以及索引。条件n < index表示跳过元素值小于索引值的连续元素。返回的结果是5、3、4,因为在遇到第一个不满足条件的元素5后,停止跳过。
- 结合其他LINQ方法使用:
List<string> fruits = new List<string> { "apple", "banana", "cherry", "date" };
IEnumerable<string> remainingFruits = fruits.Where(f => f.Length > 5).SkipWhile(f => f.StartsWith("b"));
在上述示例中,我们先使用Where方法筛选出长度大于5的水果,然后再使用SkipWhile方法跳过以字母"b"开头的连续水果。返回的结果是"cherry"、“date”,因为在遇到第一个不以"b"开头的元素"cherry"后,停止跳过。
需要注意的是,SkipWhile方法在遇到第一个不满足条件的元素后,就会停止跳过。如果所有元素都满足条件,那么将返回一个空集合
在LINQ中,Join方法用于将两个集合(内连接)或多个集合(联接)基于共享的键关联起来。Join方法通过匹配两个集合中具有相同键的元素,生成一个新的结果集。
Join方法的基本语法如下:
IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(
IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector
)
其中,outer和inner是要联接的两个集合,outerKeySelector和innerKeySelector是用于提取键的函数,resultSelector用于生成结果元素。
Join
以下是Join方法的几个用法示例:
- 内连接两个集合:
List<Person> persons = new List<Person>
{
new Person { Id = 1, Name = "Alice" },
new Person { Id = 2, Name = "Bob" },
new Person { Id = 3, Name = "Charlie" }
};
List<Salary> salaries = new List<Salary>
{
new Salary { PersonId = 1, Amount = 5000 },
new Salary { PersonId = 2, Amount = 6000 },
new Salary { PersonId = 4, Amount = 4000 }
};
var result = persons.Join(
salaries,
person => person.Id,
salary => salary.PersonId,
(person, salary) => new { Name = person.Name, Amount = salary.Amount }
);
在上述示例中,我们通过Join方法将persons和salaries两个集合内连接起来,基于Id和PersonId的匹配关系。返回的结果是一个匿名类型的集合,包含具有相匹配的Id和PersonId的元素。
- 内连接多个集合:
List<Order> orders = new List<Order>
{
new Order { OrderId = 1, Product = "Apple", Quantity = 5 },
new Order { OrderId = 2, Product = "Banana", Quantity = 3 },
new Order { OrderId = 3, Product = "Apple", Quantity = 2 },
};
List<Invoice> invoices = new List<Invoice>
{
new Invoice { InvoiceId = 1, Product = "Apple", Amount = 20 },
new Invoice { InvoiceId = 2, Product = "Banana", Amount = 15 },
new Invoice { InvoiceId = 3, Product = "Cherry", Amount = 10 },
};
List<Customer> customers = new List<Customer>
{
new Customer { CustomerId = 1, Name = "Alice" },
new Customer { CustomerId = 2, Name = "Bob" },
};
var result = customers
.Join(
orders,
customer => customer.CustomerId,
order => order.CustomerId,
(customer, order) => new { customer.Name, order.OrderId, order.Quantity }
)
.Join(
invoices,
o => o.OrderId,
invoice => invoice.InvoiceId,
(o, invoice) => new { o.Name, o.OrderId, o.Quantity, invoice.Amount }
);
在上述示例中,我们通过两个Join方法进行多个集合的内连接。首先,我们将customers和orders两个集合按照CustomerId和OrderId进行连接,然后将结果再与invoices集合按照OrderId和InvoiceId进行连接。返回的结果是一个具有联接结果的匿名类型的集合。
需要注意的是,Join方法会返回所有满足联接条件的元素。如果两个集合中没有匹配的元素,则不会生成任何结果。
GroupJoin
LINQ中的GroupJoin操作是一种用于将两个数据源进行关联的机制。它根据两个数据源中的一个或多个键来对它们进行关联,并将结果分组。
GroupJoin操作的语法如下:
var result = outer.Join(
inner,
outerKey => outerKeyProperty,
innerKey => innerKeyProperty,
(outerItem, innerItems) => new
{
Outer = outerItem,
Inner = innerItems
});
其中:
outer:外部数据源(左表)
inner:内部数据源(右表)
outerKeyProperty:外部数据源中用于关联的属性或键
innerKeyProperty:内部数据源中用于关联的属性或键
outerItem:外部数据源中的项
innerItems:与外部数据源中的项关联的内部数据源中的项
result:用于存储结果的变量
请注意,以上代码创建了一个匿名类型(new { Outer = outerItem, Inner = innerItems })作为结果的类型。可以根据需要调整选择器函数来定义结果类型。
GroupJoin操作将根据给定的键,对外部数据源和内部数据源进行关联,并生成一个包含外部数据源项和相应内部数据源项列表的结果。列表可以是空的,如果没有匹配的项。
假设我们有两个实体类,Department(部门)和Employee(员工):
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Employee
{
public int DepartmentId { get; set; }
public string Name { get; set; }
}
现在我们创建一些部门和员工的示例数据:
var departments = new List<Department>
{
new Department { Id = 1, Name = "HR" },
new Department { Id = 2, Name = "Finance" },
new Department { Id = 3, Name = "IT" }
};
var employees = new List<Employee>
{
new Employee { DepartmentId = 2, Name = "John" },
new Employee { DepartmentId = 1, Name = "Alice" },
new Employee { DepartmentId = 1, Name = "Bob" },
new Employee { DepartmentId = 3, Name = "Charlie" }
};
现在,我们可以使用GroupJoin操作将这两个数据源进行关联,并按照部门进行分组:
var result = departments.GroupJoin(
employees,
department => department.Id,
employee => employee.DepartmentId,
(department, employeeGroup) => new
{
Department = department,
Employees = employeeGroup
});
在上面的代码中,我们传入了四个参数给GroupJoin操作:
外部数据源:departments
内部数据源:employees
外部关联键:department.Id
内部关联键:employee.DepartmentId
选择器函数选择了一个匿名类型,其中包含部门对象和与部门关联的员工列表。
最后,我们可以遍历结果并输出每个部门及其对应的员工列表:
foreach (var item in result)
{
Console.WriteLine("Department: " + item.Department.Name);
Console.WriteLine("Employees: " + string.Join(", ", item.Employees.Select(e => e.Name)));
Console.WriteLine();
}
这个示例中的输出将为每个部门打印部门名称和其相应的员工列表。
OrderBy
在LINQ中,OrderBy操作用于对数据源进行升序排序。使用OrderBy操作可以根据指定的排序键对数据进行排序,并返回一个新的有序序列。
下面是OrderBy操作的示例:
假设我们有一个Person类,每个Person对象都有一个Name属性和一个Age属性:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
我们创建一些Person对象的示例数据:
var people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "John", Age = 30 },
new Person { Name = "Bob", Age = 20 },
new Person { Name = "Charlie", Age = 35 }
};
现在,我们可以使用OrderBy操作对Person对象进行按照姓名(Name)排序:
var sortedPeople = people.OrderBy(person => person.Name);
在上面的代码中,OrderBy操作传入了一个lambda表达式,通过person => person.Name指定了排序键。这意味着我们根据Person对象的Name属性进行排序。
如果我们想要按照年龄(Age)进行排序,可以使用以下代码:
var sortedPeople = people.OrderBy(person => person.Age);
同样的,OrderBy操作根据Person对象的Age属性进行排序。
最后,我们可以使用foreach循环遍历排序后的结果并对其进行输出:
foreach (var person in sortedPeople)
{
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}
这个示例中,我们遍历排序后的结果并输出每个Person对象的姓名和年龄。
注意,OrderBy操作是升序排序。如果你想进行降序排序,可以使用OrderByDescending操作。
以下是OrderBy方法的重载版本示例:
// 定义一个整数数组
int[] numbers = { 5, 2, 8, 1, 3 };
// 使用OrderBy进行降序排序
var sortedNumbers = numbers.OrderByDescending(num => num);
// 遍历排序后的结果
foreach (var num in sortedNumbers)
{
Console.WriteLine(num);
}
在上述示例中,我们使用OrderByDescending方法对整数数组numbers按照元素的降序进行排序,并将排序后的结果存储在sortedNumbers变量中。然后通过foreach循环遍历sortedNumbers,打印排序后的结果。
除了OrderByDescending方法,除了按照数字进行排序外,还可以使用其他的重载版本来进行自定义排序。例如:
// 定义一个自定义的排序规则
class PersonComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
return x.Age.CompareTo(y.Age);
}
}
// 定义一个Person类型的对象集合
List<Person> persons = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 },
new Person { Name = "Charlie", Age = 20 }
};
// 使用自定义的排序规则进行排序
var sortedPersons = persons.OrderBy(person => person, new PersonComparer());
// 遍历排序后的结果
foreach (var person in sortedPersons)
{
Console.WriteLine($"{person.Name} - {person.Age}");
}
在上述示例中,我们定义了一个自定义的排序规则PersonComparer,它实现了IComparer接口,并根据Person对象的Age属性进行排序。然后我们使用OrderBy方法,并传入自定义的排序规则,对persons集合进行排序并将结果存储在sortedPersons变量中。最后通过foreach循环遍历sortedPersons,打印排序后的结果。
OrderByDescending
LINQ的OrderByDescending方法用于对集合进行降序排序操作。它与OrderBy方法类似,但是结果是按照指定的排序规则进行降序排序,而不是升序排序。
OrderByDescending方法的语法如下:
public static IEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
参数说明:
source:要排序的源集合,类型为IEnumerable。
keySelector:一个委托,用于指定排序的关键字。它会接收源集合的每个元素作为输入,返回一个用于排序的关键字。关键字的类型可以是元素本身的类型,也可以是一个衍生类型。
返回值:
OrderByDescending方法返回一个排序后的IEnumerable类型的集合。
示例使用:
下面是一个示例,演示如何使用OrderByDescending方法对一个整数集合进行降序排序:
int[] numbers = { 5, 2, 8, 1, 3 };
var sortedNumbers = numbers.OrderByDescending(num => num);
foreach (var num in sortedNumbers)
{
Console.WriteLine(num);
}
在上述示例中,我们有一个整数数组numbers。我们使用OrderByDescending方法并提供一个lambda表达式num => num作为keySelector,它指定了按照整数本身进行排序。OrderByDescending方法返回一个排序后的IEnumerable集合,我们通过foreach循环遍历并打印排序后的结果。
自定义排序规则:
与OrderBy方法类似,OrderByDescending方法也可以自定义排序规则来进行排序操作。
以下示例演示如何按照字符串长度进行降序排序:
string[] fruits = { "orange", "apple", "banana", "cherry" };
var sortedFruits = fruits.OrderByDescending(fruit => fruit.Length);
foreach (var fruit in sortedFruits)
{
Console.WriteLine(fruit);
}
在这个示例中,我们使用OrderByDescending方法,并提供一个lambda表达式fruit => fruit.Length作为keySelector。这意味着我们将根据字符串的长度进行降序排序。OrderByDescending方法将返回一个排序后的IEnumerable集合,我们通过foreach循环遍历并打印排序后的结果。
ThenBy
LINQ的ThenBy方法用于对已经进行排序的集合进行进一步排序操作,以添加排序的次要条件。它可以在使用OrderBy或OrderByDescending方法之后进行调用,用于指定下一个排序规则。
ThenBy方法的语法如下:
public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector);
参数说明:
source:已经排序的源集合,类型为IOrderedEnumerable。通常是在使用OrderBy或OrderByDescending方法之后调用ThenBy方法。
keySelector:一个委托,用于指定排序的次要关键字。它接收源集合的每个元素作为输入,返回一个用于排序的次要关键字。关键字的类型可以是元素本身的类型,也可以是一个衍生类型。
返回值:
ThenBy方法返回一个IOrderedEnumerable类型的集合,表示已经进行了进一步排序的集合,以添加了次要排序条件。
示例使用:
下面是一个示例,展示如何使用ThenBy方法对字符串集合进行多级排序:
string[] fruits = { "apple", "banana", "cherry", "date", "elderberry" };
var sortedFruits = fruits.OrderBy(fruit => fruit.Length)
.ThenBy(fruit => fruit);
foreach (var fruit in sortedFruits)
{
Console.WriteLine(fruit);
}
在上述示例中,我们有一个字符串数组fruits。我们使用OrderBy方法对字符串集合按照长度进行升序排序,并得到一个已排序的IOrderedEnumerable类型的集合。然后我们使用ThenBy方法,并提供一个lambda表达式fruit => fruit作为keySelector,它指定了在长度相同的情况下按照字符串本身进行升序排序。ThenBy方法将返回一个进一步排序后的IOrderedEnumerable集合,我们通过foreach循环遍历并打印多级排序后的结果。
多级排序:
我们可以在ThenBy方法中调用多次,以添加多个排序条件,从而实现多级排序。
以下示例演示如何对一个Person对象集合进行多级排序:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
List<Person> persons = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 },
new Person { Name = "Alice", Age = 20 }
};
var sortedPersons = persons.OrderBy(person => person.Name)
.ThenBy(person => person.Age);
foreach (var person in sortedPersons)
{
Console.WriteLine($"{person.Name} - {person.Age}");
}
在这个示例中,我们有一个Person对象的集合。我们使用OrderBy方法对Person集合按照Name属性进行升序排序,然后使用ThenBy方法对排序结果再按照Age属性进行升序排序。最终得到一个多级排序后的IOrderedEnumerable集合,我们通过foreach循环遍历并打印排序结果。