天天看点

LINQ基础介绍背景排序过滤选择集合的并集、交叉和差异跳过和获取方法分组连接集合Join方法All和Any方法结论

我将告诉您有关嵌入式LINQ语言的基础知识。这将使您更轻松地使用C#语言。您不必使用类形式的全新工具集,而可以对现有类使用所有相同的熟悉的集合和数组。这意味着您可以充分利用LINQ查询,而对现有代码的修改很少或没有修改。LINQ to Objects功能由IEnumerable接口、序列和标准查询操作提供。

介绍

让我们看一下LINQ是什么。LINQ是语言集成查询。数据源可以是一个对象(实现IEnuberable接口,即标准集合、数组)、XML文档和DBSet数据集。但是,无论数据源如何,LINQ都采用相同的方法从该数据中获取数据。另外,LINQ有很多变体:

  • LINQ to Objects:用于处理数组和集合
  • LINQ to Entities:在通过Entity Framework技术访问数据库时使用
  • LINQ to SQL:MS SQL Server中的数据访问技术
  • LINQ to XML:在处理XML文件时使用
  • LINQ to DataSet:在使用DataSet对象时使用
  • Parallel LINQ(PLINQ):用于执行并行查询

在本文中,我首先要谈谈第一种LINQ语言。

背景

首先,为了了解它的含义,我将向您展示第一个实例:

string[] Contries = { "USA", "Canada", "United Kingdom", "Mexico", "China", "Uruguay" };
            var SelectedContr = new List<string>();
            foreach(string c in Contries)
            {
                if (c.StartsWith("U"))
                    SelectedContr.Add(c);
            }
            foreach (string s in SelectedContr)
                Console.WriteLine(s);
           

在其中,我们创建一些数据数组,然后遍历它,并在每次迭代时检查匹配项,如果是true,则写入集合。如果我说这段代码可以缩短几次并且可以删除检查条件的循环该怎么办?考虑使用LINQ语言编写的代码。要使用LINQ功能,请确保System.LINQ文件中包含命名空间。

string[] Contries = { "USA", "Canada", "United Kingdom", "Mexico", "China", "Uruguay" };
            var SelectedContr = from C in Contries
                                where c.StartsWith("U")
                                select C;
            foreach (string s in SelectedContr)
                Console.WriteLine(s);
           

我们有相同的结果,但是可以通过更简单的方式实现。该语言的定义是:

from variable in object_set
select variable;
           

因此,我们在示例中做了什么,让我们逐步进行。Countries中的C表达式将C定义为数组的每个元素,因此,我们可以用它执行各种操作。MS Visual Studio自动识别出集合c由字符串对象组成,因此C变量将被视为string。这并不意味着LINQ不是强类型的。使用where时,对象将按特定条件过滤,但在发现的情况下,对象以字母“U”开头。我们使用该select语句将所选值传递到LINQ表达式返回的结果集中。

这样的查询的优点是它们在直观上类似于SQL查询,尽管它们有一些区别。另外,除了from…In…select之外,我们还有大量的扩展方法。这是整个列表:

  • Select:定义所选值的投影
  • Where:定义选择过滤器
  • OrderBy:按升序排序项
  • OrderByDescending:以降序排序项
  • ThenBy:设置按升序排序项的其他条件
  • ThenByDescending:指定按降序排序项的其他条件
  • Join:在特定基础上加入两个集合
  • GroupBy:按键对项进行分组
  • ToLookup:按键对项进行分组,所有项目都添加到字典中
  • GroupJoin:按键执行集合加入和项分组
  • Reverse:颠倒顺序
  • All:确定集合中的所有项目是否都满足特定条件
  • Any:确定集合中的至少一个元素是否满足特定条件
  • Contains:确定集合是否包含特定元素
  • Distinct:从集合中删除重复的项
  • Except:返回两个集合的差,即仅在一个集合中创建的那些项
  • Union:结合了两个同类集合
  • Intersect:返回两个集合的交集,即两个集合中出现的那些项
  • Count:计算集合中满足特定条件的元素数
  • Sum:计算集合中数值的总和
  • Average:计算集合中数值的平均值
  • Min:找到最小值
  • Max:找到最大值
  • Take:选择一定数量的项
  • Skip:跳过一定数量的项
  • TakeWhile:返回条件序列,只要条件是 true
  • SkipWhile:只要满足给定条件,就跳过序列中的元素,然后返回其余元素
  • Concat:结合了两个集合
  • Zip:根据特定条件合并两个集合
  • First:选择集合中的第一项
  • FirstOrDefault:选择集合中的第一项或返回默认项
  • Single:选择集合中的单个元素,如果集合中包含一个以上或少于一个元素,则会引发异常
  • SingleOrDefault:选择集合中的第一项或返回默认项
  • ElementAt:在特定索引处选择序列的元素
  • ElementAtOrDefault:选择特定索引处的集合元素,或者如果索引超出范围,则返回默认值
  • Last:选择集合中的最后一项
  • LastOrDefault:选择集合中的最后一项或返回默认值

让我们仔细看一下LINQ中的一些操作。

排序

用于按升序对数据集进行排序的linq语言使用oderby 运算符。让我们看一下对数组排序的最简单的例子。

int[] num = { 5, 7, 9, 3, 19, 25, 34 };
            var numord = from j in num
                           orderby j
                           select j;
            foreach (int c in numord)
                Console.WriteLine(c);
           

默认情况下,orderby运算符按升序排序。但是,使用关键字ascending (按升序排序)和descending (按降序排序),您可以显式指定排序方向:如示例中一样,我们按数字对数组进行排序,但是该语言允许您对更复杂的对象进行排序。考虑一个例子:

class People
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
List<People> people = new List<People>
            {
                new People {Name = "Aron",Age= 19},
                new People {Name = "Bill",Age = 33},
                new People {Name = "Tom", Age = 7}
            };
            var sortedpeople = people.OrderBy(n => n.Name);
           

从示例中可以看到,我们使用了OrderBy扩展方法而不是orderby操作符。此外,您可以使用多个排序条件。考虑以下示例:

var result = from person in people
                         orderby person.Name, person.Age
                         select person;
           

在这里,我们使用了两个排序标准——name和age。

过滤选择

Where 方法用于按条件从集合中选择元素。让我们考虑一个例子:

class People
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
List<People> People = new List<People>
            {
                new People {Name = "Aron",Age= 5},
                new People {Name = "Pamela",Age = 5},
                new People {Name = "George",Age = 10},
                new People {Name = "Mark",Age = 30},
                new People {Name = "David",Age = 25},
                new People {Name = "Tim",Age = 25},
                new People {Name = "Jerry",Age = 25},
                new People {Name = "Tom", Age = 10}
            };
            IEnumerable<People> peoples = from p in People
                                          where p.Age == 25
                                          select p;
            foreach (var i in peoples)
                Console.WriteLine(i.Name);
           

在这里,我们对年龄在25的人进行了分类。使用扩展方法的相同请求:

IEnumerable<People> peoples = People.Where(i => i.Age == 25) ;
           

集合的并集、交叉和差异

除了示例方法外,LINQ还提供了几种方法,使您可以从两个对象序列(差、并集和交集)生成一组唯一元素。使用Except方法,可以获得两组的差。

string[] Female = { "Marta", "Dora", "Jane" , "Tim", "Tom", "Ben" };
            string[] Man = { "Tim", "Tom", "Ben" };

            var result = Female.Except(Man);
            foreach (string n in result)
                Console.WriteLine(n);
           

在这里,Man数组中的所有元素都将从Female数组中删除。

为了获得序列的交集,也就是说,这是两组元素共有的序列, 使用了该Intersect方法。考虑一个例子:

string[] Female = { "Marta", "Dora", "Jane", "Tim" };
            string[] Man = { "Tim", "Tom", "Ben" };

            var result = Female.Intersect(Man);
            foreach (string n in result)
                Console.WriteLine(n);
           

在这种情况下,我们将得到结果“Tim”,因为在这两个数组中只有该元素是公共的。

该Union 方法用于合并两个序列。让我们考虑一个例子:

string[] Female = { "Marta", "Dora", "Jane", "Tim" };
            string[] Man = { "Tim", "Tom", "Ben" };
            var result = Female.Union(Man);
            foreach (string n in result)
                Console.WriteLine(n);
           

结果,我们将获得一个新集合,其中包含来自第一和第二个数组的元素,并且没有重复的元素。您可以使用该Distinct 方法删除项。

跳过和获取方法

LINQ有两种方法,Skip() 并Take()允许您创建分页输出。让我们考虑一个例子:

int[] num = { 1,2,3,4,5,6,7,8,9 };
          var first = num.Take(5);
          foreach (int i in first)
              Console.WriteLine(i);

          var rest = num.Skip(5);
          foreach (int i in rest)
              Console.WriteLine(i);
           

结果,在第一种情况下,我们将采用前五个元素,在第二种情况下,我们将跳过前五个元素。但是,还有更多有趣的方法: TakeWhile()和SkipWhile()。考虑一个例子: 

int[] num = {1,1,2,3,4,5 };
            foreach (var c in num.TakeWhile(n=>n.Equals(1)))
                Console.WriteLine(c);
           

在我们的示例中,该TakeWhile()方法选择从第一个元素开始直到满足条件的元素集。该SkipWhile()方法以类似的方式工作。它从第一个元素开始循环遍历元素集,直到满足特定条件为止。

分组

若要按特定参数对数据进行分组,请使用groupby 运算符或GroupBy()方法。让我们考虑一个例子:

class People
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }

List<People> People= new List<People>
            {
                new People {Name = "Aron",Age= 5},
                new People {Name = "Pamela",Age = 5},
                new People {Name = "George",Age = 10},
                new People {Name = "Mark",Age = 30},
                new People {Name = "David",Age = 25},
                new People {Name = "Tim",Age = 25},
                new People {Name = "Jerry",Age = 25},
                new People {Name = "Tom", Age = 10}
            };

            var PeopleGroups = from person in People
                              group person by person.Age;

            foreach (IGrouping<int, People> person in PeopleGroups)
            {
                Console.WriteLine(person.Key);
                foreach (var gr in person)
                    Console.WriteLine(gr.Name);
                Console.WriteLine();
            }
           

在此示例中,我们按年龄将人员分组。有一个特殊性:如果最后一个对LINQ表达式中的选择执行操作的运算符是group, 则不应用该select语句。group 运算符接受由该分组执行的标准:group person by person.Age——在这种情况下,由Age属性分组。

该group语句导致选择包含组。每个组代表一个IGrouping <string, People>对象:string参数指示键的类型, People参数指示分组的对象的类型。

每个组都有一个键,我们可以通过Key属性获得它: person.Key

可以使用其他迭代来获取组中的所有元素。组项目的类型与传递给该group语句的对象的类型相同,在这种情况下,即为类型People的对象。可以使用GroupBy 扩展方法构建完全相同的查询。考虑一个例子:

var PeopleGroups = People.GroupBy(p => p.Age);

            foreach (IGrouping<int, People> person in PeopleGroups)
            {
                Console.WriteLine(person.Key);
                foreach (var gr in person)
                    Console.WriteLine(gr.Name);
                Console.WriteLine();
            }
           

在这里,我们应用了GroupBy 扩展方法,最终得到了相同的结果。

连接集合Join方法

如果需要将几种不同类型的集合组合为一组,可以使用join 运算符。该join操作员或Join() 方法用于加入。通常,此操作应用于具有一个共同准则的两个集合。让我们考虑一个例子:

List<Country> countries = new List<Country>
            {
                new Country{Name ="USA",Population =330},
                new Country{Name ="Canada",Population = 37}
            };

            List<Capital> capitals = new List<Capital>
            {
                new Capital {Name= "Ottawa",CountryName="Canada"},
                new Capital {Name="Washington",CountryName="USA"}
            };

            var result = from c in countries
                         join cap in capitals on c.Name equals cap.CountryName
                         select new { Name = c.Name,CapitalName= cap.Name, 
                                      Population= c.Population };

            foreach (var item in result)
                Console.WriteLine($"{item.Name} , {item.CapitalName}, {item.Population}");
           

如果该c.Name属性的值与该cap.CountryName属性的值匹配,则表达式join cap in capitals on c.Name equals cap.CountryName会将capitals列表中的cap与countries列表中的c相连。连接的结果将是一个匿名类型的对象,该对象将包含三个属性。

该Join()方法可以完成相同的操作:

var result = countries.Join(capitals,
                p => p.Name,
                t => t.CountryName,
                (p, t) => new { Name = p.Name, CapitalName = t.Name, 
                                Population = p.Population });
           

该方法具有四个参数: Join()

  • 第二个列表,我们将其与当前列表连接
  • 当前列表中对象的属性,通过该属性建立连接
  • 连接的第二个列表中对象的属性
  • 通过连接获得的新对象

All和Any方法

如果我们需要确定集合是否满足特定条件,那么我们就可以使用Any,All,Contains方法。这些方法是boolean,并声明true或false。

该All方法检查是否所有项目都满足条件。让我们考虑一个例子:

List<People> People= new List<People>
            {
                new People {Name = "Aron",Age= 5},
                new People {Name = "Pamela",Age = 5},
                new People {Name = "George",Age = 10},
                new People {Name = "Mark",Age = 30},
                new People {Name = "David",Age = 25},
                new People {Name = "Tim",Age = 25},
                new People {Name = "Jerry",Age = 25},
                new People {Name = "Tom", Age = 10}
            };

bool result = People.All(p => p.Age > 25); // true
            if (result)
                Console.WriteLine("All people have age more then 25");
            else
                Console.WriteLine("We have people with age less 25");
            Console.ReadKey();
           

在此示例中,我们检查了人们的年龄。当然,我们的年龄小于25,结果为false。

该Any 方法以类似的方式工作,仅允许您确定集合中的至少一个元素是否满足特定条件,我们使用与上一个示例相同的类和对象集,但稍有改变。

bool result = People.Any(u => u.Age > 45);
if (result)
                Console.WriteLine("We have somebody more then 45 years");
            else
                Console.WriteLine("Nobody");
           

它返回false,因为我们没有年龄大于45的了。

结论

总之,使LINQ如此强大和易于使用的部分原因是它与C#语言的紧密集成。您不必使用类形式的全新工具集,而可以对现有类使用所有相同的熟悉的集合和数组。这意味着您可以充分利用LINQ查询,而对现有代码的修改很少或没有修改。LINQ to Objects功能由IEnumerable<T>接口、序列和标准查询操作提供。

https://www.codeproject.com/Tips/5291809/LINQ-Basics