C#LINQ教程展示了如何在C#中使用语言集成查询(LINQ)。
语言集成查询(LINQ)是一组基于将查询功能直接集成到C#语言中的技术的名称。LINQ为对象(LINQtoObjects)、关系数据库(LINQtoSQL)和XML(LINQtoXML)提供一致的查询体验。
LINQ通过添加类似于SQL语句的查询表达式来扩展语言。LINQ查询表达式可用于方便地从数组、可枚举类、XML文档、关系数据库和第三方数据源中提取和处理数据。
查询表达式可用于查询和转换来自任何支持LINQ的数据源的数据。查询表达式已延迟执行。在我们遍历查询变量之前,它们不会被执行,例如,在foreach语句中。
LINQ查询可以用查询语法或方法语法编写。
LINQ扩展方法在System.Linq
命名空间中可用。
C#LINQ查询和方法语法
在LINQ中,我们可以使用查询或方法语法。Append
或Concat
等一些方法在查询语法中没有等效项。
var words = new string[] { "falcon", "eagle", "sky", "tree", "water" }; // Query syntax var res = from word in words where word.Contains('a') select word; foreach (var word in res) { Console.WriteLine(word); } Console.WriteLine("-----------"); // Method syntax var res2 = words.Where(word => word.Contains('a')); foreach (var word in res2) { Console.WriteLine(word); }
该示例使用查询和方法语法找出所有包含“a”字符的单词。
// Query syntax var res = from word in words where word.Contains('a') select word;
这是查询语法;它类似于SQL代码。
// Method syntax var res2 = words.Where(word => word.Contains('a'));
这是方法语法;这些方法可以链接起来。
$ dotnet run falcon eagle water ----------- falcon eagle water
C#LINQ元素访问
有几个辅助方法可以访问元素。
string[] words = { "falcon", "oak", "sky", "cloud", "tree", "tea", "water" }; Console.WriteLine(words.ElementAt(2)); Console.WriteLine(words.First()); Console.WriteLine(words.Last()); Console.WriteLine(words.First(word => word.Length == 3)); Console.WriteLine(words.Last(word => word.Length == 3));
在示例中,我们访问数组的元素。
Console.WriteLine(words.ElementAt(2));
我们使用ElementAt
从数组中获取第三个元素。
Console.WriteLine(words.First()); Console.WriteLine(words.Last());
我们检索数组的第一个和最后一个元素。
Console.WriteLine(words.First(word => word.Length == 3)); Console.WriteLine(words.Last(word => word.Length == 3));
First
和Last
方法也可以采用谓词。我们得到具有三个字符的第一个和最后一个元素。
$ dotnet run sky falcon water oak tea
Prepend
将一个值添加到序列的开头,Append
将一个值附加到序列的末尾。请注意,这些方法不会修改集合的元素。相反,他们使用新元素创建了集合的副本。
int[] vals = {1, 2, 3}; vals.Prepend(0); vals.Append(4); Console.WriteLine(string.Join(", ", vals)); var vals2 = vals.Prepend(0); var vals3 = vals2.Append(4); Console.WriteLine(string.Join(", ", vals3));
在示例中,我们将值添加到整数数组中。
$ dotnet run 1, 2, 3 0, 1, 2, 3, 4
C#LINQ选择
select
子句或Select
方法将序列的每个元素投射到一个新的形式中。它选择、投影和转换集合中的元素。Select
在其他语言中通常称为Map。
int[] vals = { 2, 4, 6, 8 }; var powered = vals.Select(e => Math.Pow(e, 2)); Console.WriteLine(string.Join(", ", powered)); string[] words = { "sky", "earth", "oak", "falcon" }; var wordLens = words.Select(e => e.Length); Console.WriteLine(string.Join(", ", wordLens));
在示例中,我们将一个整数数组转换为它的幂序列,并将一个单词数组转换为单词长度序列。
$ dotnet run 4, 16, 36, 64 3, 5, 3, 6
C#LINQ选择成匿名类型
投影是从返回的对象中选择特定字段。投影是使用select
子句执行的。我们可以将字段投影为匿名类型。
User[] users = { new (1, "John", "London", "2001-04-01"), new (2, "Lenny", "New York", "1997-12-11"), new (3, "Andrew", "Boston", "1987-02-22"), new (4, "Peter", "Prague", "1936-03-24"), new (5, "Anna", "Bratislava", "1973-11-18"), new (6, "Albert", "Bratislava", "1940-12-11"), new (7, "Adam", "Trnava", "1983-12-01"), new (8, "Robert", "Bratislava", "1935-05-15"), new (9, "Robert", "Prague", "1998-03-14"), }; var res = from user in users where user.City == "Bratislava" select new { user.Name, user.City }; Console.WriteLine(string.Join(", ", res)); record User(int id, string Name, string City, string DateOfBirth);
在示例中,我们选择居住在布拉迪斯拉发的用户。
var res = from user in users where user.City == "Bratislava" select new { user.Name, user.City };
通过selectnew
子句,我们创建了一个具有两个字段的匿名类型:Name
和City
。
$ dotnet run { Name = Anna, City = Bratislava }, { Name = Albert, City = Bratislava }, { Name = Robert, City = Bratislava }
C#LINQSelectMany
SelectMany
将序列展平为单个序列。
int[][] vals = { new[] {1, 2, 3}, new[] {4}, new[] {5, 6, 6, 2, 7, 8}, }; var res = vals.SelectMany(array => array).OrderBy(x => x); Console.WriteLine(string.Join(", ", res));
在示例中,我们有一个数组数组。使用SelectMany
方法,我们将二维数组展平为一维数组值。这些值也是有序的。
$ dotnet run 1, 2, 2, 3, 4, 5, 6, 6, 7, 8
在下一个示例中,我们将嵌套列表展平为一个包含唯一值的列表。
var vals = new List<List<int>> { new List<int> {1, 2, 3, 3}, new List<int> {4}, new List<int> {5, 6, 6, 7, 7} }; var res = vals.SelectMany(list => list) .Distinct() .OrderByDescending(e => e); Console.WriteLine(string.Join(", ", res));
Distinct
方法用于获取唯一值。
$ dotnet run 7, 6, 5, 4, 3, 2, 1
C#LINQ连接
Concat
方法连接两个序列。
User[] users1 = { new User("John", "Doe", "gardener"), new User("Jane", "Doe", "teacher"), new User("Roger", "Roe", "driver") }; User[] users2 = { new User("Peter", "Smith", "teacher"), new User("Lucia", "Black", "accountant"), new User("Michael", "Novak", "programmer") }; var allUsers = users1.Concat(users2); foreach (var user in allUsers) { Console.WriteLine(user); } record User(string FirstName, string LastName, string Occupation);
我们有两个用户数组。我们将它们与Concat
合并。
$ dotnet run User { FirstName = John, LastName = Doe, Occupation = gardener } User { FirstName = Jane, LastName = Doe, Occupation = teacher } User { FirstName = Roger, LastName = Roe, Occupation = driver } User { FirstName = Peter, LastName = Smith, Occupation = teacher } User { FirstName = Lucia, LastName = Black, Occupation = accountant } User { FirstName = Michael, LastName = Novak, Occupation = programmer }
C#LINQ过滤器
我们可以使用where
子句过滤数据。条件可以与&&
或||
运算符组合。
var words = new List<string> { "sky", "rock", "forest", "new", "falcon", "jewelry", "eagle", "blue", "gray" }; var query = from word in words where word.Length == 4 select word; foreach (var word in query) { Console.WriteLine(word); }
在这个例子中,我们选择所有有四个字母的单词。
$ dotnet run rock blue gray
在我们的列表中,我们有三个满足条件的单词。
在下一个示例中,我们使用||
运算符来组合条件。
var words = new List<string> { "sky", "rock", "forest", "new", "falcon", "jewelry", "small", "eagle", "blue", "gray" }; var res = from word in words where word.StartsWith('f') || word.StartsWith('s') select word; foreach (var word in res) { Console.WriteLine(word); }
在示例中,我们选择所有以“f”或“s”字符开头的单词。
$ dotnet run sky forest falcon small
在下面的示例中,我们使用&&
应用两个条件。
var cars = new List<Car> { new ("Audi", 52642), new ("Mercedes", 57127), new ("Skoda", 9000), new ("Volvo", 29000), new ("Bentley", 350000), new ("Citroen", 21000), new ("Hummer", 41400), new ("Volkswagen", 21600), }; var res = from car in cars where car.Price > 30000 && car.Price < 100000 select new { car.Name, car.Price }; foreach (var car in res) { Console.WriteLine($"{car.Name} {car.Price}"); } record Car(string Name, int Price);
在示例中,我们使用where
子句过滤汽车对象列表。我们包括所有价格在30000到100000之间的汽车。
$ dotnet run Audi 52642 Mercedes 57127 Hummer 41400
C#LINQ笛卡尔积
笛卡尔积是两个集合的乘积,以形成所有有序对的集合。
char[] letters = "abcdefghi".ToCharArray(); char[] digits = "123456789".ToCharArray(); var coords = from l in letters from d in digits select $"{l}{d}"; foreach (var coord in coords) { Console.Write($"{coord} "); if (coord.EndsWith("9")) { Console.WriteLine(); } } Console.WriteLine();
在示例中,我们创建了字母和数字的笛卡尔积。
var coords = from l in letters from d in digits select $"{l}{d}";
为了完成这个任务,我们使用了两个from
子句。
$ dotnet run a1 a2 a3 a4 a5 a6 a7 a8 a9 b1 b2 b3 b4 b5 b6 b7 b8 b9 c1 c2 c3 c4 c5 c6 c7 c8 c9 d1 d2 d3 d4 d5 d6 d7 d8 d9 e1 e2 e3 e4 e5 e6 e7 e8 e9 f1 f2 f3 f4 f5 f6 f7 f8 f9 g1 g2 g3 g4 g5 g6 g7 g8 g9 h1 h2 h3 h4 h5 h6 h7 h8 h9 i1 i2 i3 i4 i5 i6 i7 i8 i9
C#LINQ压缩包
Zip
方法从两个序列中获取元素并将它们组合起来。这些对是从相同位置的元素创建的。
string[] students = { "Adam", "John", "Lucia", "Tom" }; int[] scores = { 68, 56, 90, 86 }; var result = students.Zip(scores, (e1, e2) => e1 + "'s score: " + e2); foreach (var user in result) { Console.WriteLine(user); } Console.WriteLine("---------------"); var left = new[] { 1, 2, 3 }; var right = new[] { 10, 20, 30 }; var products = left.Zip(right, (m, n) => m * n); Console.WriteLine(string.Join(", ", products)); Console.WriteLine("---------------"); int[] codes = Enumerable.Range(1, 5).ToArray(); string[] states = { "Alabama", "Alaska", "Arizona", "Arkansas", "California" }; var CodesWithStates = codes.Zip(states, (code, state) => code + ": " + state); foreach (var item in CodesWithStates) { Console.WriteLine(item); }
在示例中,我们在三种情况下使用了Zip
方法。
string[] students = { "Adam", "John", "Lucia", "Tom" }; int[] scores = { 68, 56, 90, 86 }; var result = students.Zip(scores, (e1, e2) => e1 + "'s score: " + e2);
我们有一个学生数组和一个相应的分数数组。我们使用Zip
将两个数组组合成一个序列。e1
来自students
数组,e2
来自scores
数组;从相同的位置。
var left = new[] { 1, 2, 3 }; var right = new[] { 10, 20, 30 }; var products = left.Zip(right, (m, n) => m * n);
在这里,我们从两个数组中创建值的乘积。
int[] codes = Enumerable.Range(1, 5).ToArray(); string[] states = { "Alabama", "Alaska", "Arizona", "Arkansas", "California" }; var CodesWithStates = codes.Zip(states, (code, state) => code + ": " + state);
最后,我们为每个states
数组元素指定一个数字。
$ dotnet run Adam's score: 68 John's score: 56 Lucia's score: 90 Tom's score: 86 --------------- 10, 40, 90 --------------- 1: Alabama 2: Alaska 3: Arizona 4: Arkansas 5: California
C#LINQ内置聚合计算
LINQ允许我们计算聚合计算,例如最小值、最大值或总和。
var vals = new List<int> { 6, 2, -3, 4, -5, 9, 7, 8 }; var n1 = vals.Count(); Console.WriteLine($"There are {n1} elements"); var n2 = vals.Count(e => e % 2 == 0); Console.WriteLine($"There are {n2} even elements"); var sum = vals.Sum(); Console.WriteLine($"The sum of all values is: {sum}"); var s2 = vals.Sum(e => e > 0 ? e : 0); Console.WriteLine($"The sum of all positive values is: {s2}"); var avg = vals.Average(); Console.WriteLine($"The average of values is: {avg}"); var max = vals.Max(); Console.WriteLine($"The maximum value is: {max}"); var min = vals.Min(); Console.WriteLine($"The minimum value is: {min}");
在示例中,我们使用了Count
、Sum
、Average
、Max
和最小
方法。
$ dotnet run There are 8 elements There are 4 even elements The sum of all values is: 28 The sum of all positive values is: 36 The average of values is: 3.5 The maximum value is: 9 The minimum value is: -5
以下示例使用查询表达式。
var vals = new List<int> { 1, -2, 3, -4, 5, 6, 7, -8 }; var s = (from x in vals where x > 0 select x).Sum(); Console.WriteLine($"The sum of positive values is: {s}"); var words = new List<string> { "falcon", "eagle", "hawk", "owl" }; int len = (from x in words select x.Length).Sum(); Console.WriteLine($"There are {len} letters in the list");
在示例中,我们计算了vals
列表中正值的数量和words
列表中的字符数。
$ dotnet run The sum of positive values is: 22 There are 18 letters in the list
C#LINQ自定义聚合计算
可以使用Aggregate
计算自定义聚合计算。它在序列上应用累加器函数。
var vals = new List<int> { 6, 2, -3, 4, -5, 9, 7, 8 }; var sum = vals.Aggregate((total, next) => total + next); Console.WriteLine($"The sum is {sum}"); var product = vals.Aggregate((total, next) => total * next); Console.WriteLine($"The product is {product}");
在示例中,我们使用Aggregate
计算列表中值的总和和乘积。
$ dotnet run The sum is 28 The product is 362880
C#LINQorderby
使用OrderBy
方法或orderby
子句,我们可以对序列的元素进行排序。
int[] vals = { 4, 5, 3, 2, 7, 0, 1, 6 }; var result = from e in vals orderby e ascending select e; Console.WriteLine(string.Join(", ", result)); var result2 = from e in vals orderby e descending select e; Console.WriteLine(string.Join(", ", result2));
在示例中,我们按升序和降序对整数进行排序。ascending
关键字是可选的。
$ dotnet run 0, 1, 2, 3, 4, 5, 6, 7 7, 6, 5, 4, 3, 2, 1, 0
在下一个示例中,我们按多个字段对对象进行排序。
var users = new List<User> { new ("John", "Doe", 1230), new ("Lucy", "Novak", 670), new ("Ben", "Walter", 2050), new ("Robin", "Brown", 2300), new ("Amy", "Doe", 1250), new ("Joe", "Draker", 1190), new ("Janet", "Doe", 980), new ("Albert", "Novak", 1930), }; Console.WriteLine("sort ascending by last name and salary"); var sortedUsers = users.OrderBy(u => u.LastName).ThenBy(u => u.Salary); foreach (var user in sortedUsers) { Console.WriteLine(user); } Console.WriteLine("---------------------"); Console.WriteLine("sort descending by last name and salary"); var sortedUsers2 = users.OrderByDescending(u => u.LastName) .ThenByDescending(u => u.Salary); foreach (var user in sortedUsers2) { Console.WriteLine(user); } record User(string FirstName, string LastName, int Salary);
在示例中,首先按姓氏对用户进行排序,然后按薪水对用户进行排序。
var sortedUsers = users.OrderBy(u => u.LastName).ThenBy(u => u.Salary);
我们按姓氏对用户进行排序,然后按工资升序对用户进行排序。
var sortedUsers2 = users.OrderByDescending(u => u.LastName) .ThenByDescending(u => u.Salary);
在这里,我们先按姓氏对用户进行排序,然后按工资降序排列。
$ dotnet run sort ascending by last name and salary User { FirstName = Robin, LastName = Brown, Salary = 2300 } User { FirstName = Janet, LastName = Doe, Salary = 980 } User { FirstName = John, LastName = Doe, Salary = 1230 } User { FirstName = Amy, LastName = Doe, Salary = 1250 } User { FirstName = Joe, LastName = Draker, Salary = 1190 } User { FirstName = Lucy, LastName = Novak, Salary = 670 } User { FirstName = Albert, LastName = Novak, Salary = 1930 } User { FirstName = Ben, LastName = Walter, Salary = 2050 } --------------------- sort descending by last name and salary User { FirstName = Ben, LastName = Walter, Salary = 2050 } User { FirstName = Albert, LastName = Novak, Salary = 1930 } User { FirstName = Lucy, LastName = Novak, Salary = 670 } User { FirstName = Joe, LastName = Draker, Salary = 1190 } User { FirstName = Amy, LastName = Doe, Salary = 1250 } User { FirstName = John, LastName = Doe, Salary = 1230 } User { FirstName = Janet, LastName = Doe, Salary = 980 } User { FirstName = Robin, LastName = Brown, Salary = 2300 }
C#LINQMaxBy和MinBy
通过MaxBy
和MinBy
方法,我们可以找到给定属性的最大值和最小值。这些方法是在.NET6中引入的。
var users = new List<User> { new ("John", "Doe", 1230), new ("Lucy", "Novak", 670), new ("Ben", "Walter", 2050), new ("Robin", "Brown", 2300), new ("Amy", "Doe", 1250), new ("Joe", "Draker", 1190), new ("Janet", "Doe", 980), new ("Albert", "Novak", 1930), }; Console.WriteLine("User with max salary:"); var u1 = users.MaxBy(u => u.Salary); Console.WriteLine(u1); Console.WriteLine("User with min salary:"); var u2 = users.MinBy(u => u.Salary); Console.WriteLine(u2); record User(string FirstName, string LastName, int Salary);
在示例中,我们找到具有最高和最低工资的用户。
$ dotnet run User with max salary: User { FirstName = Robin, LastName = Brown, Salary = 2300 } User with min salary: User { FirstName = Lucy, LastName = Novak, Salary = 670 }
C#LINQ逆向
Reverse
方法反转序列中元素的顺序。(请注意,这与按降序排序不同。)
int[] vals = { 1, 3, 6, 0, -1, 2, 9, 9, 8 }; var reversed = vals.Reverse(); Console.WriteLine(string.Join(", ", reversed)); var reversed2 = (from val in vals select val).Reverse(); Console.WriteLine(string.Join(", ", reversed2));
在示例中,我们使用方法和查询语法反转数组的元素。
$ dotnet run 8, 9, 9, 2, -1, 0, 6, 3, 1 8, 9, 9, 2, -1, 0, 6, 3, 1
C#LINQ分组方式
我们可以根据某个键将数据分组。
var cars = new List<Car> { new ("Audi", "red", 52642), new ("Mercedes", "blue", 57127), new ("Skoda", "black", 9000), new ("Volvo", "red", 29000), new ("Bentley", "yellow", 350000), new ("Citroen", "white", 21000), new ("Hummer", "black", 41400), new ("Volkswagen", "white", 21600), }; var groups = from car in cars group car by car.Colour; foreach (var group in groups) { Console.WriteLine(group.Key); foreach (var car in group) { Console.WriteLine($" {car.Name} {car.Price}"); } } record Car(string Name, string Colour, int Price);
在示例中,我们将可用汽车按颜色分组。
$ dotnet run red Audi 52642 Volvo 29000 blue Mercedes 57127 black Skoda 9000 Hummer 41400 yellow Bentley 350000 white Citroen 21000 Volkswagen 21600
在下面的示例中,我们执行分组和聚合操作。
Revenue[] revenues = { new (1, "Q1", 2340), new (2, "Q1", 1200), new (3, "Q1", 980), new (4, "Q2", 340), new (5, "Q2", 780), new (6, "Q3", 2010), new (7, "Q3", 3370), new (8, "Q4", 540), }; var res = from revenue in revenues group revenue by revenue.Quarter into g select new { Quarter = g.Key, Total = g.Sum(e => e.Amount) }; foreach (var line in res) { Console.WriteLine(line); } record Revenue(int Id, string Quarter, int Amount);
我们有四个季度的收入。我们按季度对收入进行分组并对金额求和。
$ dotnet run { Quarter = Q1, Total = 4520 } { Quarter = Q2, Total = 1120 } { Quarter = Q3, Total = 5380 } { Quarter = Q4, Total = 540 }
我们可以使用where
子句对聚合数据应用过滤器。
Revenue[] revenues = { new (1, "Q1", 2340), new (2, "Q1", 1200), new (3, "Q1", 980), new (4, "Q2", 340), new (5, "Q2", 780), new (6, "Q3", 2010), new (7, "Q3", 3370), new (8, "Q4", 540), }; var res = from revenue in revenues group revenue by revenue.Quarter into g where g.Count() == 2 select new { Quarter = g.Key, Total = g.Sum(c => c.Amount) }; foreach (var line in res) { Console.WriteLine(line); } record Revenue(int Id, string Quarter, int Amount);
在示例中,我们仅选择恰好有两个收入的那些季度。
$ dotnet run { Quarter = Q2, Total = 1120 } { Quarter = Q3, Total = 5380 }
C#LINQ块
chunk方法将序列的元素拆分为给定大小的块。
var vals = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 }; IEnumerable<int[]> chunks = vals.Chunk(2); int res = 0; foreach (var chunk in chunks) { res += chunk[0] * chunk[1]; } Console.WriteLine(res);
在程序中,我们计算以下表达式:x1*x2+x3*x4…
$ dotnet run 100
C#LINQ词频
在下一个示例中,我们计算文件中单词的频率。
$ wget https://raw.githubusercontent.com/janbodnar/data/main/the-king-james-bible.txt
我们使用钦定版圣经。
using System.Text.RegularExpressions; var fileName = "/home/janbodnar/Documents/the-king-james-bible.txt"; var text = File.ReadAllText(fileName); var matches = new Regex("[a-z-A-Z']+").Matches(text); var words = matches.Select(m => m.Value).ToList(); var res = words .GroupBy(m => m) .OrderByDescending(g => g.Count()) .Select(x => new { word = x.Key, Count = x.Count() }) .Take(10); foreach (var r in res) { Console.WriteLine($"{r.word}: {r.Count}"); }
我们计算KingJames圣经中单词的出现频率。
var matches = new Regex("[a-z-A-Z']+").Matches(text); var words = matches.Select(m => m.Value).ToList();
我们使用Matches
方法找到所有匹配项。从匹配集合中,我们将所有单词放入一个列表中。
var res = words .GroupBy(m => m) .OrderByDescending(g => g.Count()) .Select(x => new { word = x.Key, Count = x.Count() }) .Take(10);
单词按频率降序分组和排序。我们取第一个热门词。
$ dotnet run the 62103 and 38848 of 34478 to 13400 And 12846 that 12576 in 12331 shall 9760 he 9665 unto 8942
C#LINQ连接
join
子句连接序列。
string[] basketA = { "coin", "book", "fork", "cord", "needle" }; string[] basketB = { "watches", "coin", "pen", "book", "pencil" }; var res = from item1 in basketA join item2 in basketB on item1 equals item2 select item1; foreach (var item in res) { Console.WriteLine(item); }
示例中有两个数组。使用join
子句,我们找到两个数组中都存在的所有项目。
$ dotnet run coin book
两个数组中都包含硬币和书本单词。
C#LINQ分区
Skip
方法从序列的开头跳过指定数量的元素并返回剩余的元素。SkipLast
方法返回省略指定最后数量元素的序列元素。只要指定的条件为真,SkipWhile
就会依次跳过元素,然后返回剩余的元素。
Take
方法从序列的开头返回指定数量的连续元素。TakeLast
方法忽略除指定数量的最后元素之外的所有元素。只要指定的条件为真,TakeWhile
方法就会返回序列中的元素,然后跳过剩余的元素。
int[] vals = { 1, 2, 7, 8, 5, 6, 3, 4, 9, 10 }; var res1 = vals.Skip(3); Console.WriteLine(string.Join(", ", res1)); var res2 = vals.SkipLast(3); Console.WriteLine(string.Join(", ", res2)); var res3 = vals.SkipWhile(e => e < 5); Console.WriteLine(string.Join(", ", res3)); Console.WriteLine("----------"); var res4 = vals.Take(3); Console.WriteLine(string.Join(", ", res4)); var res5 = vals.TakeLast(3); Console.WriteLine(string.Join(", ", res5)); var res6 = vals.TakeWhile(e => e < 5); Console.WriteLine(string.Join(", ", res6));
该示例使用了所有六种分区方法。
$ dotnet run 8, 5, 6, 3, 4, 9, 10 1, 2, 7, 8, 5, 6, 3 7, 8, 5, 6, 3, 4, 9, 10 ---------- 1, 2, 7 4, 9, 10 1, 2
C#LINQ转换
我们可以将返回的枚举转换为列表、数组或字典。
User[] users = { new (1, "John", "London", "2001-04-01"), new (2, "Lenny", "New York", "1997-12-11"), new (3, "Andrew", "Boston", "1987-02-22"), new (4, "Peter", "Prague", "1936-03-24"), new (5, "Anna", "Bratislava", "1973-11-18"), new (6, "Albert", "Bratislava", "1940-12-11"), new (7, "Adam", "Trnava", "1983-12-01"), new (8, "Robert", "Bratislava", "1935-05-15"), new (9, "Robert", "Prague", "1998-03-14"), }; string[] cities = (from user in users select user.City).Distinct().ToArray(); Console.WriteLine(string.Join(", ", cities)); Console.WriteLine("------------"); List<User> inBratislava = (from user in users where user.City == "Bratislava" select user).ToList(); foreach (var user in inBratislava) { Console.WriteLine(user); } Console.WriteLine("------------"); Dictionary<int, string> userIds = (from user in users select user).ToDictionary(user => user.id, user => user.Name); foreach (var kvp in userIds) { Console.WriteLine($"{kvp.Key}: {kvp.Value}"); } record User(int id, string Name, string City, string DateOfBirth);
我们对数据源执行三个查询;生成的枚举被转换为列表、数组和字典。
string[] cities = (from user in users select user.City).Distinct().ToArray();
在此查询中,我们从数据源中选择所有城市。我们应用Distinct
方法,最后调用ToArray
方法。
List<User> inBratislava = (from user in users where user.City == "Bratislava" select user).ToList();
这里我们得到了居住在布拉迪斯拉发的用户列表;我们调用ToList
方法。
Dictionary<int, string> userIds = (from user in users select user).ToDictionary(user => user.id, user => user.Name);
在此查询中,我们将用户名及其ID转换为字典。
$ dotnet run London, New York, Boston, Prague, Bratislava, Trnava ------------ User { id = 5, Name = Anna, City = Bratislava, DateOfBirth = 1973-11-18 } User { id = 6, Name = Albert, City = Bratislava, DateOfBirth = 1940-12-11 } User { id = 8, Name = Robert, City = Bratislava, DateOfBirth = 1935-05-15 } ------------ 1: John 2: Lenny 3: Andrew 4: Peter 5: Anna 6: Albert 7: Adam 8: Robert 9: Robert
C#LINQ生成序列
Range
、Repeat
和Empty
方法可用于生成序列。
var res = Enumerable.Range(1, 10).Select(e => Math.Pow(e, 3)); Console.WriteLine(string.Join(", ", res)); Console.WriteLine("------------------"); int[] vals = { 8, 4, 3, 2, 5, 11, 15, 10, 3, 5, 6 }; var lines = vals.Select(e => Enumerable.Repeat("*", e)).ToArray(); foreach (var line in lines) { Console.WriteLine(string.Join("", line)); } Console.WriteLine("------------------"); int[] nums = { 1, 3, 2, 3, 3, 3, 4, 4, 10, 10 }; var powered = nums.Aggregate(Enumerable.Empty<double>(), (total, next) => total.Append(Math.Pow(next, 2))); foreach (var val in powered) { Console.WriteLine(val); }
在示例中,我们使用Range
、Repeat
和Empty
方法生成序列。
var res = Enumerable.Range(1, 10).Select(e => Math.Pow(e, 3));
使用Range
,我们生成从1到10的整数并将它们立方。
int[] vals = { 8, 4, 3, 2, 5, 11, 15, 10, 3, 5, 6 }; var lines = vals.Select(e => Enumerable.Repeat("*", e)).ToArray();
借助Repeat
方法,我们为vals
数组中的每个值生成一个水平条。
var powered = nums.Aggregate(Enumerable.Empty<double>(), (total, next) => total.Append(Math.Pow(next, 2)));
我们使用Empty
方法为Aggregate
方法创建一个空序列。
$ dotnet run 1, 8, 27, 64, 125, 216, 343, 512, 729, 1000 ------------------ ******** **** *** ** ***** *********** *************** ********** *** ***** ****** ------------------ 1 9 4 9 9 9 16 16 100 100
C#LINQ量词
使用量词,我们检查某些条件。
var vals = new List<int> { -1, -3, 0, 1, -3, 2, 9, -4 }; bool positive = vals.Any(x => x > 0); if (positive) { Console.WriteLine("There is a positive value"); } bool allPositive = vals.All(x => x > 0); if (allPositive) { Console.WriteLine("All values are positive"); } bool hasSix = vals.Contains(6); if (hasSix) { Console.WriteLine("6 value is in the array"); }
使用Any
方法,我们检查列表中的任何元素是否为正值。使用All
方法,我们检查列表中的所有元素是否都是正数。最后,我们使用Contains
方法确定列表是否包含值6。
C#LINQ集合操作
LINQ具有执行集合操作的方法,包括Union
、Intersect
、Except
和Distinct
。
var vals1 = "abcde".ToCharArray(); var vals2 = "defgh".ToCharArray(); var data = vals1.Union(vals2); Console.WriteLine("{" + string.Join(" ", data) + "}"); var data2 = vals1.Intersect(vals2); Console.WriteLine("{" + string.Join(" ", data2) + "}"); var data3 = vals1.Except(vals2); Console.WriteLine("{" + string.Join(" ", data3) + "}"); int[] nums = { 1, 1, 2, 3, 4, 4, 4, 5, 6, 7, 7, 8 }; var data4 = nums.Distinct(); Console.WriteLine("{" + string.Join(" ", data4) + "}");
在示例中,我们对数组元素进行设置操作。
var vals1 = "abcde".ToCharArray(); var vals2 = "defgh".ToCharArray();
借助ToCharArray
,我们创建了两个字符数组。
var data = vals1.Union(vals2);
Union
生成两个数组的集合并集。
var data2 = vals1.Intersect(vals2);
Intersect
生成两个数组的集合交集。
var data3 = vals1.Except(vals2);
Except
产生两个数组的集合差异。
var data4 = nums.Distinct();
Distinct
从数组中返回不同的元素。换句话说,它从数组创建一个集合。
$ dotnet run {a b c d e f g h} {d e} {a b c} {1 2 3 4 5 6 7 8}
C#LINQXML
LINQ可用于处理XML。
using System.Xml.Linq; string myXML = @" <Users> <User> <Name>Jack</Name> <Sex>male</Sex> </User> <User> <Name>Paul</Name> <Sex>male</Sex> </User> <User> <Name>Frank</Name> <Sex>male</Sex> </User> <User> <Name>Martina</Name> <Sex>female</Sex> </User> <User> <Name>Lucia</Name> <Sex>female</Sex> </User> </Users>"; var xdoc = new XDocument(); xdoc = XDocument.Parse(myXML); var females = from u in xdoc.Root.Descendants() where (string)u.Element("Sex") == "female" select u.Element("Name"); foreach (var e in females) { Console.WriteLine("{0}", e); }
我们解析XML数据并选择所有女性名字。
$ dotnet run <Name>Martina</Name> <Name>Lucia</Name>
在本文中,我们在C#中使用了LINQ。
列出所有C#教程。