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。
按任意鍵關閉此窗口. . .
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章