16 C# 第十四章 标准查询运算符

定义

IEnumerable<T> 上的每个方法都是一个标准查询运算符(Standard Query Operator)。 它提供了对集合进行查询的能力。

注意查询运算符查询表达式是有区别的。


这里有一个简单的例子

A simple sample show how to use.

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

namespace LinqSample01
{
    class Program
    {
        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} }
        };

        static void Main(string[] args)
        {
            IEnumerable<Student> studentQuery = students.Where(
                    student => student.Scores[0] > 90
                );

            foreach (Student item in studentQuery)
            {
                Console.WriteLine("Name: {0}  :    {1}", item.Last, item.Scores[0]);
            }
            Console.ReadKey();
        }
    }
}




关于 Linq 查询运算符的知识点。


    -  关于 IEnumerable<T> 

        .NET 中集合本质是一个类,这个类的关键是实现了 IEnumerable<T> 的接口。要实现对集合的遍历
        关键就是要实现 IEnumerable<T> 中规定的方法。


        IEnumerator 和 IEnumerable<T>的迭代器遍历。
        这个与C++ STL的迭代器想法差不多,对于一个集合来说如果我们知道它具体元素的个数,我们可以使用
        for (int i = 0; i < array.length; i++) 的(length-index)方式来实现遍历,但对于不知道具体元素的集合来说,
        比较合适的方式是从第一个到最后一个,逐个的进行遍历。IEnumerator实现的目标就是允许使用迭代器方式来遍历。




    -  集合初始化器

        // 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}},
           ...  ...
        };


        这段代码用了初始化数组的方式初始化了一个集合 List<Student>,但按照正常来说集合一定是在实例化好以后才能添加成员的。
        其实这里使用了 C# 3.0一个新增的特性 集合初始化器。


        集合初始化器要想编译成功,最理想的情况是集合支持 System.Collections.Generic.ICollection<T> 接口,这样可以保证
        集合支持 Add() 方法。


        并不是所有的集合都支持集合初始化器,原因有两个:
        1)  并不是所有的集合都实现了ICollection<T> 接口,换个角度看就是这个接口并不是对每个集合都有用。
        2)  由于方法名的匹配,例如 一个集合初始化器 支持 new DataStore(){a, {b,c}},这就表示 add()方法
            要支持两种接口 add(a) 和 add(b, c),接口多样化会给程序带来方便,但有时也是负担。




    -  Linq 函数

        where() 筛选
            IEnumerable<Student> studentQuery = students.Where(
                    student => student.Scores[0] > 90
                );

        注意:  where() 方法的表达式实参并非一定是在赋值时求值的,只有在需要遍历集合项时,才会对表达式求值。


        select() 投射
       select 子句可以指定将在执行查询时产生的值的类型
       在最简单的情况下,select子句仅指定范围变量。这会使返回的序列包含与数据源具有相同类型的元素。
       使用 select 进行结果转换:

       把查询结果转换为xml格式 (http://msdn.microsoft.com/zh-cn/library/bb397914(v=vs.90).aspx)

class XMLTransform
{
	static void Main()
	{            
		// Create the data source by using a collection initializer.
		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}},
		};

		// Create the query.
		var studentsToXML = new XElement("Root",
			from student in students
			let x = String.Format("{0},{1},{2},{3}", student.Scores[0],
					student.Scores[1], student.Scores[2], student.Scores[3])
			select new XElement("student",
					   new XElement("First", student.First),
					   new XElement("Last", student.Last),
					   new XElement("Scores", x)
					) // end "student"
				); // end "Root"

		// Execute the query.
		Console.WriteLine(studentsToXML);

		// Keep the console open in debug mode.
		Console.WriteLine("Press any key to exit.");
		Console.ReadKey();
	}
}


        Count() 计数
        用来获取集合上元素个数的计数操作。功能很简单,用起来要小心。
        在实例的代码中有两个打印Count数组的地方。
            Console.WriteLine("ICollection<T> Count: ", students.Count());
            Console.WriteLine("IEnumerable<T> Count: ", studentQuery.Count());


        它们作用差不多,都是看看一共有多少个元素,但差别在于ICollection<T>有Count属性,但IEnumerable<T>
        会去枚举整个的集合。

        如果只是想判断一下计数是否大于 0 ---> if (IEnumerable<T>.Count() > 0) {...  ...}
        那么还是替换一下吧,if (IEnumerable<T>.any()) {...  ...},这里只会尝试遍历其中的一个元素。


    -  推迟执行 --- 查询的重复执行

        这是一个非常重要的概念,先看一段代码,代码很简单,想要说明的问题就是在某些情况下当我们并
        不想遍历整个集合时,由于IEnumerable<T>的推迟执行,在一些意想不到的地方我们还是对整个集合进行了遍历。

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

namespace DelayExecute
{
    class Program
    {
        static void Main(string[] args)
        {
            // Specify the data source.
            int[] scores = new int[] { 97, 92, 81, 60 };
            bool result = false;

            IEnumerable<int> scoreQuery = scores.Where(
                score => 
                {
                    result = false;
                    if (score > 80)
                    {
                        result = true;
                        Console.WriteLine("Score = {0}", score);
                    }
                    return result;    
                });

            Console.WriteLine("===============================================");
            Console.WriteLine("1)  Invoke empty foreeach, it will execute");
            foreach (int i in scoreQuery)
            {
            }

            Console.WriteLine("===============================================");
            Console.WriteLine("2)  Invoke IEnumerable Count, it will execute");
            scoreQuery.Count();

            Console.WriteLine("===============================================");
            Console.WriteLine("3)  Invoke IEnumerable ToArray, it will execute");

            scoreQuery = scoreQuery.ToArray();
            Console.ReadKey();
        }
    }
}


        1) foreach:foreach 的本质是调用 MoveNext(),这是会触发遍历的开关。


        2) Count:在前面讨论过,IEnumerable<T>的Count并不是一个属性,而是一个函数。


        3) ToArray:ToXXX是个好东西,可以把集合变成可以安全操作的对象,但它同样会触发遍历,而且会把结果都加载到内存中。

        下面的时序图是对推迟执行的一个简单的总结。

sequence_of_delay

其他的一些查询函数

这些函数和SQL中的函数使用比较类似,可以用SQL的想法帮助理解。


OrderBy() 和 ThenBy()

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

namespace TestOrderAPI
{
    class Program
    {
        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} }
        };

        static void Main(string[] args)
        {
            IEnumerable<Student> studentQuery = students.OrderBy(
                    student => student.Scores[0]).ThenBy(student => student.Scores[1]);

            foreach (Student item in studentQuery)
            {
                Console.WriteLine("Name: {0}  :    {1},   {2}", item.Last, item.Scores[0], item.Scores[1]);
            }

            Console.ReadKey();
        }
    }
}


Join()

用于把两个集合用某中关键字连接起来。这里用的是 TeamID

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

namespace TestJoinAPI
{
    class Program
    {
        public class Student
        {
            public string First { get; set; }
            public string Last { get; set; }
            public int ID { get; set; }
            public List<int> Scores;
            public int TeamID { get; set; }
        }

        public class Team
        {
            public int TeamID { get; set; }
            public string TeamName { get; set; }
        }

        // 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}, TeamID=0},
           new Student {First="Claire",   Last="O’Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}, TeamID=1},
           new Student {First="Sven",     Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}, TeamID=0},
           new Student {First="Cesar",    Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}, TeamID=1},
           new Student {First="Debra",    Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}, TeamID=0},
           new Student {First="Fadi",     Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}, TeamID=1},
           new Student {First="Hanying",  Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}, TeamID=0},
           new Student {First="Hugo",     Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}, TeamID=1},
           new Student {First="Lance",    Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}, TeamID=0},
           new Student {First="Terry",    Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}, TeamID=1},
           new Student {First="Eugene",   Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}, TeamID=0},
           new Student {First="Michael",  Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}, TeamID=1 }
        };

        // Create a data source by using a collection initializer.
        static List<Team> teams = new List<Team>
        {
            new Team {TeamID = 0, TeamName="Team A"},
            new Team {TeamID = 1, TeamName="Team B"},
        };

        static void Main(string[] args)
        {
            var items = students.Join(teams,
                    student => student.TeamID,    // The key one for join
                    team => team.TeamID,          // The key two for join
                    (student, team) => new        // The new collection items
                    {
                        team.TeamID,
                        team.TeamName,
                        student.ID,
                        student.First,
                    }).OrderBy(team => team.TeamID);

            foreach (var item in items)
            {
                Console.WriteLine("Team {0} :  {1}    {2}", item.TeamName, item.ID, item.First);
            }

            Console.ReadKey();
        }
    }
}


GroupBy()

用来对具有相似特征的对象进行分组。

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

namespace TestGroupByAPI
{
    class Program
    {
        public class Student
        {
            public string First { get; set; }
            public string Last { get; set; }
            public int ID { get; set; }
            public List<int> Scores;
            public int TeamID { get; set; }
        }

        // 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}, TeamID=0},
           new Student {First="Claire",   Last="O’Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}, TeamID=1},
           new Student {First="Sven",     Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}, TeamID=0},
           new Student {First="Cesar",    Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}, TeamID=1},
           new Student {First="Debra",    Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}, TeamID=0},
           new Student {First="Fadi",     Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}, TeamID=1},
           new Student {First="Hanying",  Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}, TeamID=0},
           new Student {First="Hugo",     Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}, TeamID=1},
           new Student {First="Lance",    Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}, TeamID=0},
           new Student {First="Terry",    Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}, TeamID=1},
           new Student {First="Eugene",   Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}, TeamID=0},
           new Student {First="Michael",  Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}, TeamID=1 }
        };

        static void Main(string[] args)
        {
            IEnumerable<IGrouping<int, Student>> StudentGroup = students.GroupBy(
                    (student) => student.TeamID);

            foreach (IGrouping<int, Student> group in StudentGroup)
            {
                Console.WriteLine("==========================================");
                foreach (Student stu in group)
                {
                    Console.WriteLine("Team: {0}  Name: {1}", stu.TeamID, stu.First);
                }
                Console.WriteLine("==========================================");
            }

            Console.ReadKey();
        }
    }
}



GroupJoin()

如果我们想知道一个小组中有多少个学生,这是一个一对多的映射,但 Join是一个一对一的映射,调用后还需要再次处理。

这时使用 GroupJoin() 是最合理的。他把小组ID 与 一组学生(多名学生)对应了起来。

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

namespace TestGroupJoinAPI
{
    class Program
    {
        public class Student
        {
            public string First { get; set; }
            public string Last { get; set; }
            public int ID { get; set; }
            public List<int> Scores;
            public int TeamID { get; set; }
        }

        public class Team
        {
            public int TeamID { get; set; }
            public string TeamName { get; set; }
        }

        // 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}, TeamID=0},
           new Student {First="Claire",   Last="O’Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}, TeamID=1},
           new Student {First="Sven",     Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}, TeamID=0},
           new Student {First="Cesar",    Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}, TeamID=1},
           new Student {First="Debra",    Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}, TeamID=0},
           new Student {First="Fadi",     Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}, TeamID=1},
           new Student {First="Hanying",  Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}, TeamID=0},
           new Student {First="Hugo",     Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}, TeamID=1},
           new Student {First="Lance",    Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}, TeamID=0},
           new Student {First="Terry",    Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}, TeamID=1},
           new Student {First="Eugene",   Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}, TeamID=0},
           new Student {First="Michael",  Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91}, TeamID=1 }
        };

        // Create a data source by using a collection initializer.
        static List<Team> teams = new List<Team>
        {
            new Team {TeamID = 0, TeamName="Team A"},
            new Team {TeamID = 1, TeamName="Team B"},
        };

        static void Main(string[] args)
        {
            var items = teams.GroupJoin(
                    students,
                    team => team.TeamID,
                    student => student.TeamID,
                    (team, studentGroup) => new
                    {
                        team.TeamID,
                        team.TeamName,
                        students = studentGroup
                    });

            foreach (var item in items)
            {
                Console.WriteLine("Team {0} : ", item.TeamName);

                foreach(Student stu in item.students)
                {
                    Console.WriteLine("Name: {0}, {1}", stu.First, stu.Last);
                }
                Console.WriteLine("====================================================");
            }

            Console.ReadKey();
        }
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值