visual C#(二十一)使用查询表达式来查询内存中的数据

参考书:《 visual C# 从入门到精通》
第三部分 用C#定义可扩展类型
第21章 使用查询表达式来查询内存中的数据

21.1 什么是LINQ

LINQ,Language INtegrated Query,语言集成查询,对应用程序代码中查询数据的机制进行“抽象”。

LINQ的语法和语义和SQL很像。但LINQ更灵活,且能处理范围更大的逻辑数据结构。

21.2 在C#应用程序中使用LINQ

LINQ要求数据用实现了IEnumerableiEnumerable<T>接口的数据结构进行存储。可以使用数组、HashSet、Queue或其他集合类型,要求这种类型是可枚举的。如:

var customers=new[]{
    new{CustomerID=1,FirstName="Kim",LastName="Abercromble",CompanyName="Alpine Ski House"},
    ...;
};
var addresses=new[]{
    new{CompanyName="Alpine Ski House",City="Berne",Country="Switzerland"},
    ...;
}

21.2.1 选择数据

一下代码显式由customers数组中FirstName组成的列表:

IEnumerable<string>customerFirstName=customers.Select(cust=>cust.FirstName);
foreach(string name in customerFirstName){
    Console.WriteLine(name);
}

Select方法允许从数组获取特定信息,传给Select方法的参数是另一个方法。注意:

  • cust 是传给方法的参数
  • Select方法目前还没有开始获取数据,它只是返回一个可枚举的对象。之后遍历时才会真正获取由Select指定的数据
  • Select不是Array类型的方法,它是Enumerable类的扩展方法。Enumerable类位于System.Linq命名空间,它提供了大量的静态方法来查询实现了泛型IEnumerable<T>接口的对象

Select方法的定义如下:

public static IEnumerable<TResult> Select<TSource.TResult>(
this.IEnumerable<TSource> source,Func<TSource,TResult> selector)

表明Select是泛型方法,需要获取两个类型参数和两个普通参数。TSource是要为其生成可枚举结果的集合,TResult是可枚举结果集中的数据。Select是扩展方法。

简单的说就是Select方法返回基于某具体类型的可枚举集合。

如果需要返回多个数据项,例如需要返回FirstName和LastName,有下面几个方案可选:

  • 将二者的字符串连接起来:IEnumerable<string> customerNames=customers.Select(cust=>$"{cust.FirstName} {cust.LastName}");
  • 可以定义一种新类型来封装名字和姓氏:
class FullName{
    public string FirstName{get;set;}
    public string LastName{get;set;}
}
...;
IEnumerable<Names>customerName=customers.Select(cust=>new FullName{
    FirstName=cust.FirstName,LastName=cust.LastName
});
  • 也可以使用匿名类型,就不需要专门定义一个新的类型了,var customerName=customers.Select(cust=>new{FirstName=cust.FirstName,LastName=cust.LastName});

21.2.2 筛选数据

Where方法筛选数据:

IEnumerable<string> usCompanies=address.Where(addr=>String.Equals(addr.Country,"United states"))
    .Select(usComp=>usComp.CompanyName);
foreach(string name in usCompanies){
    Console.WriteLine(name);
}

21.2.3 排序、分组和聚合数据

OrderBy:

IEnumberable<string>companyNames=address.OrderBy(addr=>addr.CompanyName).Select(comp.CompanyName);
foreach(string name in companyName)
    COnsole.WriteLine(name);

要降序的话用OrderByDescending。要按多个键来排序可以在OrderByOrderByDescending后使用ThenByThenByDescending

按一个或多个字段中共同的值对数据进行分组,可以使用GroupBy

var companiesGroupByCountry=addresses.GroupyBy(addrs=>addrs.Country);
foreach(var companiesPerCountry in companiesGroupByCountry){
    Console.WriteLine($"Country:{companiesPerCountry.Key}\t{companiesPerCountry.Count()}companies");
    foreach(var companies in companiesPerCountry){
    	Console.WriteLine($"\t{companies.CompanyName}");
	}
}

GroupBy后面不用Select将字段投射到结果。

可以直接为Select方法的结果使用很多汇总的方法,如CountMaxMin等。

int num=addresses.Select(addr=>addr.CompanyName).Count();

Console.WriteLine($"Number of companies: {num}");

注意Count是不会区分重复的值。如果要不重复计数,可以用Distinct方法删除重复:

int num=addresses.Select(addr=>addr.Country).Distinct().Count();
Console.WriteLine($"Number of countries :{num}");

21.2.4 联结数据

var com=customers.Select(c=>new{c.FirstName,c.LastName,c.CompanyName})
    .Join(addresses,custs=>custs.CompanyName,addrs=>addrs.CompanyName,
         (custs,addrs)=>new{custs.FirstName,custs.LastName,addrs.Country});
foreach(var row in com){
    Console.WriteLine(row);
}

21.2.5 使用查询操作符

用查询操作符fromselect

var cust=from cust in customers
    select cust.FirstName;

编译器会将上述表达式解析成对应的Select方法。

var cust=from c in customers
    select new {c.FirstName,c.LastName};
var usCompanies=from a in addresses
    where String.Equals(a.Country,"United States")
    select a.CompanyName;
var companyNames=from a in addresses
    orderby a.CompanyName
    select a.CompanyName;
var comp=from a in addresses
    group a by a.Country;
int num=(from a in addresses
        select a.CompanyName).Count();
int nums=(from a in addresses
         select a.Country).Distrinct().Count();
var citiesAndCustomers=from a in addresses
    join c in customers
    on a.CompanyName equals c.CompanyName
    select new{c.FirstName,c.LastName,a.Country};

21.2.6 查询Tree<TItem>对象中的数据

下面是简单的实例,用查询表达式来查询我们的二叉树中的数据。

先新建一个简答的类:

```employee.cs`

using System;
using System.Collections.Generic;
using System.Text;

namespace C_21_2_6
{
    class Employee:IComparable<Employee>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Department { get; set; }
        public int Id { get; set; }
        public override string ToString()
        {
            return $"Id; {this.Id}, Name: {this.FirstName} {this.LastName}, Dept: {this.Department}";
        }

        int IComparable<Employee>.CompareTo(Employee other)
        {
            if (other == null)
                return 1;
            if (this.Id > other.Id)
                return 1;
            if (this.Id < other.Id)
                return -1;
            return 0;
        }
    }
}

可以通过右击项目添加现有项的方式添加之前创建的二叉树类Tree.cs

Program.cs中的代码如下:

using c_19_1_1;
using System;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;

namespace C_21_2_6
{
    class Program
    {
        static void dowork()
        {
            Tree<Employee>emptree=new Tree<Employee>(
                new Employee { Id=1,FirstName="Kim",LastName="Abercromble",Department="IT"});
            emptree.Insert(new Employee { Id = 2, FirstName = "Jeff", LastName = "Hay", Department = "Marketting" });
            emptree.Insert(new Employee { Id = 4, FirstName = "Charlie", LastName = "Herb", Department = "IT" });
            emptree.Insert(new Employee { Id = 6, FirstName = "Chris", LastName = "Preston", Department = "Sales" });
            emptree.Insert(new Employee { Id = 3, FirstName = "Dava", LastName = "Barnett", Department = "Sales" });
            emptree.Insert(new Employee { Id = 5, FirstName = "Tim", LastName = "Litton", Department = "Marketing" });
            Console.WriteLine("List of departments");
            var depts = emptree.Select(d => d.Department).Distinct();
            foreach (var dept in depts)
                Console.WriteLine($"Department: {dept}");
            Console.WriteLine("\nEmployees in the IT department");
            var ITEmployees = emptree.Where(e => String.Equals(e.Department, "IT")).Select(emp => emp);
            foreach (var emp in ITEmployees)
                Console.WriteLine(emp);
            Console.WriteLine("\nAll employees grouped by department");
            var employeesByDept = emptree.GroupBy(e => e.Department);
            foreach(var dept in employeesByDept)
            {
                Console.WriteLine($"Department :{dept.Key}");
                foreach (var emp in dept)
                    Console.WriteLine($"\t{emp.FirstName} {emp.LastName}");
            }
            Console.WriteLine("\n查询操作符:\n");
            //var depts = emptree.Select(d => d.Department).Distinct();
            var deptss = (from d in emptree
                          select d.Department).Distinct();
            foreach (var dept in deptss)
                Console.WriteLine($"Department: {dept}");
            Console.WriteLine("\nEmployees in the IT department");
            //var ITEmployees = emptree.Where(e => String.Equals(e.Department, "IT")).Select(emp => emp);
            var ITemployeess = from e in emptree
                               where String.Equals(e.Department, "IT")
                               select e;
            foreach (var emp in ITemployeess)
                Console.WriteLine(emp);
            Console.WriteLine("\nAll employees grouped by department");

            //var employeesByDept = emptree.GroupBy(e => e.Department);
            var employeesByDepts = from e in emptree
                                   group e by e.Department;
            foreach (var dept in employeesByDepts)
            {
                Console.WriteLine($"Department :{dept.Key}");
                foreach (var emp in dept)
                    Console.WriteLine($"\t{emp.FirstName} {emp.LastName}");
            }
            


        }
        static void Main(string[] args)
        {
            dowork();
        }
    }
}

运行结果为:

List of departments
Department: IT
Department: Marketting
Department: Sales
Department: Marketing

Employees in the IT department
Id; 1, Name: Kim Abercromble, Dept: IT
Id; 4, Name: Charlie Herb, Dept: IT

All employees grouped by department
Department :IT
        Kim Abercromble
        Charlie Herb
Department :Marketting
        Jeff Hay
Department :Sales
        Dava Barnett
        Chris Preston
Department :Marketing
        Tim Litton

查询操作符:

Department: IT
Department: Marketting
Department: Sales
Department: Marketing

Employees in the IT department
Id; 1, Name: Kim Abercromble, Dept: IT
Id; 4, Name: Charlie Herb, Dept: IT

All employees grouped by department
Department :IT
        Kim Abercromble
        Charlie Herb
Department :Marketting
        Jeff Hay
Department :Sales
        Dava Barnett
        Chris Preston
Department :Marketing
        Tim Litton

C:\Users\xhh\Source\Repos\C_21_2_6\C_21_2_6\bin\Debug\netcoreapp3.1\C_21_2_6.exe (进程 2008)已退出,代码为 0。
按任意键关闭此窗口. . .

21.2.7 LINQ和推迟求值

注意一点,从执行一个LINQ查询到遍历集合之间,原始集合中的数据有可能会发生变化的,但遍历获取的结果始终是根据最新的数据的结果。所以如果我们先定义一个LINQ查询,然后修改集合,最后获取数据时得到最新的数据,这个策略就是推迟求值

如我们将刚才的应用程序中的Program.cs中的代码修改一下来验证这一点:

using c_19_1_1;
using System;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;

namespace C_21_2_6
{
    class Program
    {
        static void dowork()
        {
            Tree<Employee>emptree=new Tree<Employee>(
                new Employee { Id=1,FirstName="Kim",LastName="Abercromble",Department="IT"});
            emptree.Insert(new Employee { Id = 2, FirstName = "Jeff", LastName = "Hay", Department = "Marketting" });
            emptree.Insert(new Employee { Id = 4, FirstName = "Charlie", LastName = "Herb", Department = "IT" });
            emptree.Insert(new Employee { Id = 6, FirstName = "Chris", LastName = "Preston", Department = "Sales" });
            emptree.Insert(new Employee { Id = 3, FirstName = "Dava", LastName = "Barnett", Department = "Sales" });
            emptree.Insert(new Employee { Id = 5, FirstName = "Tim", LastName = "Litton", Department = "Marketing" });
            Console.WriteLine("All employees");
            var allemployees = from e in emptree
                               select e;
            foreach (var emp in allemployees)
                Console.WriteLine(emp);
            emptree.Insert(new Employee { Id = 7, FirstName = "David", LastName = "Simpson", Department = "IT" });
            Console.WriteLine("\nEmployee added");
            Console.WriteLine("All employees");
            foreach (var emp in allemployees)
                Console.WriteLine(emp);
        }
        static void Main(string[] args)
        {
            dowork();
        }
    }
}

运行结果:

All employees
Id; 1, Name: Kim Abercromble, Dept: IT
Id; 2, Name: Jeff Hay, Dept: Marketting
Id; 3, Name: Dava Barnett, Dept: Sales
Id; 4, Name: Charlie Herb, Dept: IT
Id; 5, Name: Tim Litton, Dept: Marketing
Id; 6, Name: Chris Preston, Dept: Sales

Employee added
All employees
Id; 1, Name: Kim Abercromble, Dept: IT
Id; 2, Name: Jeff Hay, Dept: Marketting
Id; 3, Name: Dava Barnett, Dept: Sales
Id; 4, Name: Charlie Herb, Dept: IT
Id; 5, Name: Tim Litton, Dept: Marketing
Id; 6, Name: Chris Preston, Dept: Sales
Id; 7, Name: David Simpson, Dept: IT

C:\Users\xhh\Source\Repos\C_21_2_6\C_21_2_6\bin\Debug\netcoreapp3.1\C_21_2_6.exe (进程 10036)已退出,代码为 0。
按任意键关闭此窗口. . .
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值