第三十五讲 LINQ 语法(二)

回顾上节:
    1. from语句:查询用,在一个查询中如需要查询多个数据源,则使用多个from
        注意这个多数据源的意思,除第一个数据源外,追加的数据源可以是上个数据源的变量,也可以是有别于上一级数据源的其它数据源。
    
    2. 类型推断:LINQ查询最初的数据类型由数据源决定,但最后的的数据类型由select子句决定
    
    3. select语句:定义最终要查询的数据以及将结果插入到结果序列中。
        注意这里的,查询结果可能是一个变量也可以是一个集合,这个集合可以是变量也可以是一个匿名对象或者说是一个投影。
    
    4. Group语句:分组,将查询结果按照指定分组条件放入不同的组中。
    
    5. Into关键字:使用into上下文关键创建一个临时标识符,以便将group, join, 或 select子句的结果存储到新的标识符中。
    
    6. Let子句:在查询增加一个范围变量。可以是直接运算来的, 也可以调用外部方法。
    
本节主要内容:
    连接查询
    1. join子句:将来自不同源序列并且在对象模型中没有直接关系的元素相关联
    要求:每个源中的元素需要共享某个可以进行比较以判断是否相等的值
    equals 关键字:比较指定的键是否相等
    join子句的输出形式取决于所执行的连接的具体类型。以下是三种最常见的连接类型:
    
        1. 内部连接
        如:
            var class_stu4=
                from stu in students
                join cls in class on stu.Cid equals cls.Id
                select new{
                    Id=stu.Id,
                    Name=stu.Name,
                    Cname=cls.Name
                };
                
            其中:join cls in class on stu.Cid 语句中,join关键字连接,on关键字指示对比条件,equals关键字比较两个字段。
            同时注意:两个数据源class和students,及equals两个边的字段的左右顺序,左边是第一个查询中的字段,右边为第二个查询中的字段(实践中可以试着调换后的结果是什么样的)。
            
        2. 分组连接
            含有into表达式的join子句称为分组连接
        如:
            var class_stu4=
                from cls in class
                join stu in students on cls.Id equals stu.Cid into groupstu
                select new{
                    Name=cls.Name,
                    Stu=groupstu //这里是集合哦
                };
                
            注意:上例中第一个查询是class,第二个是students,相应equals关键字两边字段的顺序也要换
            
        3. 左外连接
            左外连接中,将返回左侧源序列中的所有元素。
            若要在LINQ中执行左外连接,需要将DefaultIfEmpty方法与分组连接结合起来。
            
        如:
            var class_stu5=
                from cls in class
                join stu in students on cls.Id equals stu.Cid into groupstu
                from item in groupstu.DefaultIfEmpty()
                select new{
                    Cname=cls.Name,
                    stus=item //这是一个学生对象,当为空时报错
                };
                
            或
                var class_stu5=
                from cls in class
                join stu in students on cls.Id equals stu.Cid into groupstu
                from item in groupstu.DefaultIfEmpty(new Student {
                    Id=0,
                    Name="本班级下没有学生",
                    Cid=0
                })
                select new{
                    Cname=cls.Name,
                    stus=item.Name //这里是字段
                };
                
                
    更多的LINQ运算
        1. 聚合运算
            聚合运算即是从值集合计算出单个值
            方法有:
            1. Aggregate:对集合值执行自定义聚合运算
            2. Average:计算集合平均值
            3. Count:对集合中的元素进行计数,还可以仅对满足某一谓词函数的元素进行计数
            4. LongCount:对大型集合中的元素进行计数,还可以仅对满足某一谓词函数的元素进行计数
            5. Max:确定集合中的最大值
            6. Min:确定集合中的最小值
            7. Sum:计算集合中值的总和
            
        如:
            var students=new {
                name="张三",
                score=new List<int>{50,60,80,90}
            };//使用匿名类作为数据源
            
            double stu_avg=(
                from sco in students.score
                select sco
            ).Average();
            
            
            
        2. 串联运算(两个物理文件内容串联)
            注意新控件的使用:openFileDialog
            注意texbox控件的MultiLine属性,即多行内容显示,或可输入多行内容。
            
            如:合并文件
                if(openFileDialog1.ShowDialog()==DialogResult.OK)
                {
                    tb_a.Text=openFileDialog1.FileName;
                }//如果打开的文件访问对话框返回的值是OK, 则将文件的名字赋值给文本控件的内容属性。
                
                if(openFileDialog1.ShowDialog()==DialogResult.OK)
                {
                    tb_b.Text=openFileDialog1.FileName;
                }//解释同上
                
                //接下来是合并两个文件的内容
                string txtA=System.IO.File.ReadAllText(tb_a.Text);//这里是表示将文本中的内容全部读入到变量
                string txtB=System.IO.File.ReadAllText(tb_b.Text);
                
                //下面这两句是将文本内容按逗号进行分隔,然后装入数组变量中。
                string[] strsA=txtA.Split(',');
                string[] strsB=txtB.Split(',');
                
                IEnumerable<string> concatQuery=
                    strsA.Concat(strsB).OrderBy(t => t).Distinct();//注意关键字Concat是用于连接字符串数组的,OrderBy是用于排序的,Distinct是去除重复内容的关键字
                    
                StringBuilder sb=new StringBuilder();
                foreach(string s in concatQuery)
                {
                    sb.AppendFormat("{0},",s)
                }
                textBox4.Text=sb.ToString();
                
        
        3. 类型转换
        如:
            List<string> names=
            (
                from stu in students
                select stu.Name
            ).Cast<string>().ToList<string>();
            
            注意:关键字Cast类型转换功能的用法
            
            
            
        4. lambda与LINQ
            lambda表达式树允许开发人员像处理数据一样对lambda表达式进行修改。
            
            参数 => 语句或语句块
            
            <x,y> => x*y //多参数,隐式类型 => 表达式
            
            x => x*5 //单参数,隐式类型转换 => 表达式
            
            x => {return x*5} //单参数,隐式类型 => 语句块
            
            (int x) => x*5 //单参数,显式类型 => 表达式
            
            (int x) => {return x*5} //单参数,显式类型 => 表达式
            
            () => Consol.WriteLine() //无参数
        
            用法:
            int[] arry={1,3,5,6,7,8,5};
            
            //LINQ的写法
            var res=
                from i in arry
                select i;
                
            //Lambda写法
            var res=arry.Select(i => i);
            
            //LINQ的写法
            var res=
                from s in arry
                where s%2==0
                select s;
                
            //Lambda写法
            var res=arry.where(s=>s%2==0);
            
            Lambda表达式其实就是一个匿名委托,可以使用Lambda表达式的地方必须是可以使用委托的地方。
        
        5. 表达式树
            编写Lambda表达式树
            Lambda表达式树就是Lambda表达式转换成树状结构
            
            Func<int,int> func=pra => pra * pra
            //创建表达式树
            
            Expression<Func<int,int>> expression = pra => pra*pra
            //创建表达式树
            
            当编译器编译Lambda表达式时,如果Lambda表达式使用的是Func方法,则编译器会将Lambda表达式直接编译成匿名方法,而如果Lambda表达式使用的是Expression方法,则编译器会将Lambda表达式进行分析,处理然后得到一种数据结构。

 

不废话看实例:

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

namespace Con35
{
    class Program
    {
        static void Main(string[] args)
        {
            //跟前面几个例子似的,先创建几个对象集合出来
            //说实话,老师讲的我都看的懂,但是。。。。看的懂真的不一定会写
            //一碰键盘脑袋空空啊,还是认认真真的自己敲出来实惠些
            List<Banji> bjs = new List<Banji>() { 
                new Banji("1001","小学一年级"),
                new Banji("1002","小学二年级"),
                new Banji("1003","小学三年级"),
                new Banji("1004","小学四年级"),
                new Banji("1005","小学五年级"),
                new Banji("1006","初中一年级"),
                new Banji("1007","初中二年级"),
                new Banji("1008","初中三年级"),
                new Banji("1009","初中四年级")
            };
            List<student> stus = new List<student>() { 
                new student(1,"张小三",new List<int>(){33,78,90},"1001"),
                new student(2,"王小三",new List<int>(){65,66,67},"1002"),
                new student(3,"李小三",new List<int>(){54,78,67},"1003"),
                new student(4,"赵小三",new List<int>(){65,32,21},"1004"),
                new student(5,"刘小三",new List<int>(){90,78,90},"1005"),
                new student(6,"田小三",new List<int>(){65,78,88},"1006"),
                new student(7,"张中三",new List<int>(){65,44,90},"1007"),
                new student(8,"王中三",new List<int>(){55,78,90},"1008"),
                new student(9,"张小四",new List<int>(){65,78,89},"1008"),
                new student(20,"张小五",new List<int>(){65,44,77},"1005"),
                new student(10,"张小六",new List<int>(){32,78,90},"1004"),
                new student(11,"张小七",new List<int>(){65,55,66},"1007"),
                new student(12,"张小八",new List<int>(){70,78,77},"1002")
            };

            Console.WriteLine("\n========join...on...equals.===============");
            var stuCollections = from bj in bjs
                                 where bj.bjNum != "1005"//这里加个排除班级编号为1005的班级
                                 join st in stus on bj.bjNum equals st.bjNum
                                 select new { na = st.stuName, bn = bj.bjName, bu = bj.bjNum };
            foreach (var v in stuCollections)
            {
                Console.WriteLine("学生名({0}, 班级名({1})),班级编号是({2})", v.na, v.bn, v.bu);
            }

            Console.WriteLine("\n========join...on...equals...into===============");
            var stuCol = from bj in bjs
                         where bj.bjName != "初中三年级"
                         join st in stus on bj.bjNum equals st.bjNum into col
                         select new { id = bj.bjNum, bn = bj.bjName, col };
            foreach (var v in stuCol)
            {
                //需要进行两次迭代,啥是迭代?啥是循环?一个接一个不用报告情况直到解决问题,而循环是一个又一个,即
                //时时回报情况,这是我对迭代和循环的理解
                Console.WriteLine("\n=====班级编号({0}),班级名字({1})======\n", v.id, v.bn);
                //迭代学生集合
                foreach (student s in v.col)
                {
                    Console.WriteLine("学生编号({0}),学生姓名({1}),学生所在班级({2})", s.stuId, s.stuName, s.bjNum);
                }
            }
            Console.WriteLine("\n====外连接相关,必须要考虑到空值,未加默认值=======");
            //这里我复制了join...on... into中的代码进来,稍加改动
            var stuCols = from bj in bjs
                          where bj.bjName != "初中三年级"
                          join st in stus on bj.bjNum equals st.bjNum into col//下面加了段空值判断语句
                          from item in col.DefaultIfEmpty()//这里没有对其指定默认值
                          select new { id = bj.bjNum, bn = bj.bjName, test = item };
            foreach (var v in stuCols)
            {
                Console.WriteLine("\n=====班级编号({0}),班级名字({1})======\n", v.id, v.bn);
                //Console.WriteLine("学生编号({0}),学生姓名({1}),学生所在班级({2})", v.test.stuId, v.test.stuName, v.test.bjNum);//如果item为Null那运行时就会报错,我还自以为不会,实践出真知,真理也!
                Console.WriteLine("学生编号({0}),学生姓名({1}),学生所在班级({2})", v.test != null ? v.test.stuId : 0, v.test != null ? v.test.stuName : "无", v.test != null ? v.test.bjNum : "空空空");//这里为每个对象属性加个条件判断
            }

            Console.WriteLine("\n====外连接相关,必须要考虑到空值,追加默认值=======");
            //这里我复制了join...on... into中的代码进来,稍加改动
            var stuCol2 = from bj in bjs
                          where bj.bjName != "初中三年级"
                          join st in stus on bj.bjNum equals st.bjNum into col//下面加了段空值判断语句
                          from item in col.DefaultIfEmpty(
                            new student(0,"万能的神",new List<int>(){0,0,0},"无")
                          )//指定默认值
                          select new { id = bj.bjNum, bn = bj.bjName, test = item };
            foreach (var v in stuCol2)
            {
                Console.WriteLine("班级信息({0}号,{1}),学生信息({2}号学生 {3},所属班级编号是({4}))"
                    ,v.id,v.bn,v.test.stuId,v.test.stuName,v.test.bjNum
                    );
            }

            Console.WriteLine("\n======聚合函数=======");
            //老师很细心的又让我复习了一下匿名类
            var stu = new
            {
                name = "朱八戒",
                score = new List<int>() { 90, 45, 98, 34, 78, 99 }
            };
            //不知道有没有人不明白,下面为什么我这么肯定要用double数据类型?
            //这里面有个关系,上面stu这个对象类型决定了val的类型
            //接下来,slect的类型决定了maxValue的数据类型
            //而maxValue的类型就决定了foreach或着输出的数据类型
            double maxValue = (from val in stu.score 
                               select val).Max();

            Console.WriteLine("最大值是{0}", maxValue.ToString());

            Console.ReadKey();
        }
    }

    public class Banji
    {
        public string bjNum;//班级编号

        public string bjName;//班级名字

        public Banji(string bnu, string bna)
        {
            this.bjName = bna;
            this.bjNum = bnu;
        }
    }

    public class student
    {
        public int stuId;//学生编号
        public string stuName;//学生姓名
        public List<int> score;//学生成绩单
        public string bjNum;//班级编号

        public student(int sid, string sna, List<int> sco, string bjn)
        {
            this.stuId = sid;
            this.stuName = sna;
            this.score = sco;
            this.bjNum = bjn;
        }
    }
}

后的实例待补充,这是今天在工作业务练习的,后面的以窗口程序来做。   

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值