LINQ(五)演练


演练:用 C# 编写查询 (LINQ)

创建C# 项目

创建项目

1.    启动 VisualStudio

2.    在菜单栏上,选择“文件”“新建“项目”

将打开“新建项目”对话框。

3.    外接 已安装,展开 模板,展开 Visual C#,然后选择 控制台应用程序

4.     名称 文本框中,输入一个不同的名称或接受默认名称,然后选择 确定 按钮。

新项目出现在“解决方案资源管理器”中。

5.    请注意,您的项目具有对 System.Core.dll的引用以及适用于 System.Linq 命名空间的 using 指令。

创建内存中数据源

查询的数据源是 Student 对象的简单列表。 每个 Student 记录都有名、姓和表示他们在班级中的测验分数的整数数组。 将此代码复制到您的项目。 请注意下列特性:

·        Student 类包含自动实现的属性。

·        列表中的每个学生都已使用对象初始值设定项进行初始化。

·        列表本身已使用集合初始值设定项进行初始化。

将在不显式调用任何构造函数和使用显式成员访问的情况下初始化并实例化整个数据结构。 有关这些新功能的更多信息,请参见自动实现的属性(C#编程指南)对象和集合初始值设定项(C#编程指南)

添加数据源

·         Student 类和经过初始化的学生列表添加到您的项目的 Program 类中。

C#

 
public class Student
{
    public string First { get; set; }
    public string Last { get; set; }
    public int ID { get; set; }
    public List<int> Scores;
}
 
// Create a data source by using a collection initializer.
static List<Student> students = new List<Student>
{
   new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
   new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
   new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
   new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
   new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
   new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
   new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
   new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
   new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
   new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
   new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
   new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91} }
};
 
 

在学生列表中添加新学生

·        将新 Student 添加到 Students 列表中并使用您选择的姓名和测验分数。 尝试键入新学生的所有信息,以便更好地了解对象初始值设定项的语法。

创建查询

创建简单查询

·        在应用程序的 Main 方法中创建一个简单查询,执行该查询时,将生成第一次测验中分数高于 90 分的所有学生的列表。 请注意,由于选择了整个 Student 对象,因此查询的类型是IEnumerable<Student> 虽然代码也可以通过使用 var 关键字来使用隐式类型,但这里使用了显式类型以便清楚地演示结果。 (有关 var 的更多信息,请参见隐式类型的局部变量(C#编程指南)。)

另请注意,该查询的范围变量 student 用作对源中每个 Student 的引用,以提供对每个对象的成员访问。

C#

 
// Create the query.
// The first line could also be written as "var studentQuery ="
IEnumerable<Student> studentQuery =
    from student in students
    where student.Scores[0] > 90
    select student;
 
 

执行查询

执行查询

1.    现在编写用于执行查询的 foreach 循环。 请注意有关代码的以下事项:

o    所返回序列中的每个元素是通过 foreach 循环中的迭代变量来访问的。

o    此变量的类型是 Student,查询变量的类型是兼容的 IEnumerable<Student>

2.    添加此代码后,按Ctrl + F5生成并运行该应用程序,然后在“控制台”窗口中查看结果。

C#

 
// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
    Console.WriteLine("{0}, {1}", student.Last, student.First);
}
 
// Output:
// Omelchenko, Svetlana
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael
 
 

添加另一个筛选条件

·        您可以在 where 子句中组合多个布尔条件,以便进一步细化查询。 下面的代码添加一个条件,以便查询返回第一个分数高于 90 分并且最后一个分数低于 80 分的那些学生。 where 子句应类似于以下代码。

·         where student.Scores[0] > 90 && student.Scores[3] < 80

有关更多信息,请参见where子句(C#参考)

修改查询

对结果进行排序

1.    如果结果按某种顺序排列,则浏览结果会更容易。 您可以根据源元素中的任何可访问字段对返回的序列进行排序。 例如,下面的 orderby 子句根据每个学生的姓按从 A 到 Z 的字母顺序对结果进行排序。 紧靠在 where 语句之后、select 语句之前,将下面的 orderby 子句添加到您的查询中:

2.  orderby student.Last ascending

3.    现在更改 orderby 子句,以便根据第一次测验的分数的反向顺序,即从高分到低分的顺序对结果进行排序。

4.  orderby student.Scores[0] descending

5.    更改 WriteLine 格式字符串以便您可以查看分数:

6.  Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);

有关更多信息,请参见orderby子句(C#参考)

对结果进行分组

1.    分组是查询表达式中的强大功能。 包含 group子句的查询将生成一系列组,每个组本身包含一个Key 和一个序列,该序列由该组的所有成员组成。 下面的新查询使用学生的姓的第一个字母作为关键字对学生进行分组。

C#

 
// studentQuery2 is an IEnumerable<IGrouping<char, Student>>
var studentQuery2 =
    from student in students
    group student by student.Last[0];
 
 

2.    请注意,查询的类型现在已更改。 该查询现在生成一系列将 char 类型作为关键字的组,以及一系列 Student 对象。 由于查询的类型已更改,因此下面的代码也会更改 foreach 执行循环:

C#

 
// studentGroup is a IGrouping<char, Student>
foreach (var studentGroup in studentQuery2)
{
    Console.WriteLine(studentGroup.Key);
    foreach (Student student in studentGroup)
    {
        Console.WriteLine("   {0}, {1}",
                  student.Last, student.First);
    }
}
 
// Output:
// O
//   Omelchenko, Svetlana
//   O'Donnell, Claire
// M
//   Mortensen, Sven
// G
//   Garcia, Cesar
//   Garcia, Debra
//   Garcia, Hugo
// F
//   Fakhouri, Fadi
//   Feng, Hanying
// T
//   Tucker, Lance
//   Tucker, Michael
// A
//   Adams, Terry
// Z
//   Zabokritski, Eugene
 
 

3.    按 Ctrl +F5运行该应用程序并在“控制台”窗口中查看结果。

有关更多信息,请参见group子句(C#参考)

隐式类型化变量

·        显式编写 IGroupings  IEnumerables 代码很快就会变得乏味。 通过使用 var,您可以更方便地编写相同的查询和 foreach 循环。 var 关键字不会更改对象的类型,它仅指示编译器推断类型。 更改 studentQuery 的类型和迭代变量的 group var 并重新运行查询。 请注意,在内部foreach 循环中,迭代变量仍然类型化为 Student,而查询仍可像以前一样工作。  s 迭代变量更改为 var 并再次运行查询。 您将看到完全相同的结果。

C#

 
var studentQuery3 =
    from student in students
    group student by student.Last[0];
 
foreach (var groupOfStudents in studentQuery3)
{
    Console.WriteLine(groupOfStudents.Key);
    foreach (var student in groupOfStudents)
    {
        Console.WriteLine("   {0}, {1}",
            student.Last, student.First);
    }
}
 
// Output:
// O
//   Omelchenko, Svetlana
//   O'Donnell, Claire
// M
//   Mortensen, Sven
// G
//   Garcia, Cesar
//   Garcia, Debra
//   Garcia, Hugo
// F
//   Fakhouri, Fadi
//   Feng, Hanying
// T
//   Tucker, Lance
//   Tucker, Michael
// A
//   Adams, Terry
// Z
//   Zabokritski, Eugene
 
 

有关 var 的更多信息,请参见隐式类型的局部变量(C#编程指南)

按键值对组进行排序

·        当您运行前面的查询时,会注意到组没有按字母顺序排列。 若要改变这种情况,您必须在 group子句后提供 orderby 子句。 但要使用 orderby 子句,您首先需要一个标识符,用作对 group子句创建的组的引用。 可以使用 into 关键字来提供此标识符,如下所示:

C#

 
var studentQuery4 =
    from student in students
    group student by student.Last[0] into studentGroup
    orderby studentGroup.Key
    select studentGroup;
 
foreach (var groupOfStudents in studentQuery4)
{
    Console.WriteLine(groupOfStudents.Key);
    foreach (var student in groupOfStudents)
    {
        Console.WriteLine("   {0}, {1}",
            student.Last, student.First);
    }
}
 
// Output:
//A
//   Adams, Terry
//F
//   Fakhouri, Fadi
//   Feng, Hanying
//G
//   Garcia, Cesar
//   Garcia, Debra
//   Garcia, Hugo
//M
//   Mortensen, Sven
//O
//   Omelchenko, Svetlana
//   O'Donnell, Claire
//T
//   Tucker, Lance
//   Tucker, Michael
//Z
//   Zabokritski, Eugene
 
 

当您运行此查询时,就会看到组现在已按字母顺序排序。

使用 let 引入标识符

·        您可以使用 let 关键字为查询表达式中的任何表达式结果引入标识符。 此标识符可以提供方便(如下面的示例所示),也可以通过存储表达式的结果来避免多次计算,从而提高性能。

C#

 
// studentQuery5 is an IEnumerable<string>
// This query returns those students whose
// first test score was higher than their
// average score.
var studentQuery5 =
    from student in students
    let totalScore = student.Scores[0] + student.Scores[1] +
        student.Scores[2] + student.Scores[3]
    where totalScore / 4 < student.Scores[0]
    select student.Last + " " + student.First;
 
foreach (string s in studentQuery5)
{
    Console.WriteLine(s);
}
 
// Output:
// Omelchenko Svetlana
// O'Donnell Claire
// Mortensen Sven
// Garcia Cesar
// Fakhouri Fadi
// Feng Hanying
// Garcia Hugo
// Adams Terry
// Zabokritski Eugene
// Tucker Michael
 
 

有关更多信息,请参见let子句(C#参考)

在查询表达式中使用方法语法

·        LINQ中的查询语法和方法语法 (C#) 中所述,一些查询操作只能使用方法语法表示。 下面的代码计算源序列中每个 Student 的总分,然后对该查询的结果调用 Average() 方法来计算班级的平均分。 请注意,查询表达式的两边使用了括号。

C#

 
var studentQuery6 =
    from student in students
    let totalScore = student.Scores[0] + student.Scores[1] +
        student.Scores[2] + student.Scores[3]
    select totalScore;
 
double averageScore = studentQuery6.Average();
Console.WriteLine("Class average score = {0}", averageScore);
 
// Output:
// Class average score = 334.166666666667
 
 

在 select 子句中转换或投影

1.    查询生成的序列的元素与源序列中的元素不同,这种情况很常见。 删除或注释掉您之前的查询和执行循环,并用下面的代码替换它。 请注意,该查询返回一个字符串(而非 Students)序列,这种情况将反映在 foreach 循环中。

C#

 
IEnumerable<string> studentQuery7 =
    from student in students
    where student.Last == "Garcia"
    select student.First;
 
Console.WriteLine("The Garcias in the class are:");
foreach (string s in studentQuery7)
{
    Console.WriteLine(s);
}
 
// Output:
// The Garcias in the class are:
// Cesar
// Debra
// Hugo
 
 

2.    本演练前面的代码指出班级的平均分约为 334分。 若要生成总分高于班级平均分的 Students 及其 Student ID 的序列,可以在 select 语句中使用匿名类型:

C#

 
var studentQuery8 =
    from student in students
    let x = student.Scores[0] + student.Scores[1] +
        student.Scores[2] + student.Scores[3]
    where x > averageScore
    select new { id = student.ID, score = x };
 
foreach (var item in studentQuery8)
{
    Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score);
}
 
// Output:
// Student ID: 113, Score: 338
// Student ID: 114, Score: 353
// Student ID: 116, Score: 369
// Student ID: 117, Score: 352
// Student ID: 118, Score: 343
// Student ID: 120, Score: 341
// Student ID: 122, Score: 368
 
 

后续步骤

熟悉了在 C# 中使用查询的基本情况后,便可以开始阅读您感兴趣的具体类型的 LINQ提供程序的文档和示例:

LINQ to SQL [LINQ to SQL]

LINQ to DataSet

LINQ to XML

LINQ to Objects

 

支持 LINQ 的 C# 功能

查询表达式

var query = from str in stringArray

           group str by str[0] into stringGroup

           orderby stringGroup.Key

           select stringGroup;

隐式类型化变量 (var)

var number = 5;

var name = "Virginia";

var query = from str in stringArray

           where str[0] == 'm'

           select str;

匿名类型

select new {name = cust.Name, phone =cust.Phone};

 

Lambda 表达式

Lambda 表达式是一种内联函数,该函数使用 => 运算符将输入参数与函数体分离,并且可以在编译时转换为委托或表达式树。 在LINQ 编程中,在您对标准查询运算符进行直接方法调用时,会遇到 lambda 表达式。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赫曦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值