那天有个小孩跟我说LINQ(一) 转载
1 LINQ准备(代码下载)
新建项目 linq_Ch1控制台程序,新建一个Entity文件夹
1.1 对象初始化器
在Entity新建一个类Student,代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace linq_Ch1.Entity { public class Student { /// <summary> /// 学生编号 /// </summary> public int Id { get; set; } /// <summary> /// 学生姓名 /// </summary> public string StudentName { get; set; } /// <summary> /// 生日 /// </summary> public DateTime Birthday { get; set; } } }
在Program.cs文件代码添加如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using linq_Ch1.Entity; namespace linq_Ch1 { class Program { static void Main(string[] args) { //1 对象初始化器 2013年4月1日22:14:52 List<Student> students = new List<Student>(); students.Add(new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}); students.Add(new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }); students.Add(new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now }); Console.WriteLine("用对象初始化器方式:"+students[1].StudentName);
Console.ReadLine(); } } }
1.2 集合初始化器
//2 集合初始化器 2013年4月1日22:19:06 List<Student> students_2 = new List<Student> { new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}, new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }, new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now } }; Console.WriteLine("用集合初始化器方式:"+students[1].StudentName); //Dictionary版本 Dictionary<string, Student> student_dic = new Dictionary<string, Student> { {"达一",new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}}, {"坤二", new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }}, {"张三",new Student { Id=3,StudentName="张三",Birthday=DateTime.Now}}, }; Console.WriteLine("用集合初始化器方式Dictionary版本:" + student_dic["达一"].StudentName);
1.3 创建隐身类型的局部变量
使用var关键字,用is判断类型,不能初始化为null,不能同一语句中初始化多个隐式类型的变量
//3.使用var关键字,就是不显示声明的类型 var var_int = 1; var var_double = 2.22; var var_string = "茗洋芳竹"; Console.WriteLine("var_int是数字吗?"+(var_int is int)); Console.WriteLine("var_double是浮点型数字吗?" + (var_double is double)); Console.WriteLine("var_string是字符串吗?" + (var_string is string));
//4.使用var保存查询Student的结果 List<Student> stu = new List<Student>(); for (int i = 1; i < 10; i++) { stu.Add(new Student { Id=i,StudentName="学生"+i.ToString(),Birthday=DateTime.Now}); } var query = from o in stu where o.Id < 5 select new { 学生编号 = o.Id, 学生姓名 = o.StudentName }; foreach (var item in query) { Console.WriteLine(item.学生编号+":"+item.学生姓名); }
1.4创建隐型数组
①数组初始化器中的所有元素不能隐式转换为同一类型的数据,就会产生编译错误。
②其他
//创建隐型数组 var arr1 = new[] {0,1,2,3,4,5,6,7,8,9,10 }; var arr2 = new[,] { {"1","小A","xa"},{"2","小B","xb"},{"3","小C","xc"}}; Console.WriteLine(arr1[1]); //1 Console.WriteLine(arr2[1,1]); //小B
1.5创建匿名类型对象(很重要)
//6.创建匿名类型的对象 var StudentInfo = new { Id=1,StudentName="陈四",Birthday=DateTime.Now}; Console.WriteLine("生日是:"+StudentInfo.Birthday.ToString("yyyy年MM月dd日"));
1.6 Lambda表达式(超重要)
在void main方法上面声明几个委托,如下
然后添加代码如下:
//7 lambda AddOne myAddOne; //参数的多少跟委托的签名的参数相等 myAddOne = x => x + 1;//隐式声明一个参数,表达式方法体 myAddOne = (int x) => x + 1;//显示声明一个参数,表达式方法体 ToAdd myToAdd = (x, y) => x + y; //lambda表达式支持用表达式或语句块作为方法体,语法上比匿名方法更加灵活(匿名方法的方法体只能是语句块) Method1 myMethod=()=>Console.WriteLine("myToAdd"); //无参数,表达式方法体 myMethod(); //调用myMethod方法
1.8 用语句作为Lambda表达式的方法体
//8.用语句作为Lambda表达式的方法体 myAddOne = x => { return x + 1; }; myAddOne = (int x) => { return x + 1; }; Console.WriteLine("正确答案应该是5:,你的答案是"+myAddOne(4));
1.9 扩展方法-创建自己的where操作符
①扩展Datetime方法
//扩展方法 static class ExtendMethod{ public static string ToYYYYMMDD(this DateTime dt)//自身必须是静态方法,第一个参数必须是this,后面紧跟要扩展的类的名称 { return dt.ToString("yyyyMMdd"); } }
然后我们在void main方法中调用
//9 扩展方法 Console.WriteLine(DateTime.Now.ToYYYYMMDD()); Console.ReadLine();
②创建MyWhere操作符,扩展方法MyWhere的第一个参数类型是IEnumerable<TSource>,表示该方法作用于所有实 现IEnumerable<TSource>接口的类。第二个参数是Func<TSource,bool>类型,表示第二个参数 接受匿名方法或者Lambda表达式。在该方法中首先使用foreach遍历第一个参数传入的序列,然后逐一判断序列中的元素是否满足第二个参数传入的匿 名方法或Lambda的表达式条件
继续在ExtendMoehtod方法中添加如下代码
public static IEnumerable<TSource> MyWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { foreach (TSource s in source) { if(predicate(s)) yield return s; } }
执行foreach循环过程中,每次遇到yield return语句时,该方法都将会向调用者(foreach循环)返回一个值,调用者收到该值后进行相应的处理(如 Console.Write),随后将控制权交回给迭代器方法MyWhere。再由迭代器方法给出下一个元素。yield关键字不需要CLR提供支持,它 是由编译器实现的,编译器生成实现迭代器的必要代码。
调用方法
//9 扩展方法 Console.WriteLine("==============扩展方法地盘============="); Console.WriteLine(DateTime.Now.ToYYYYMMDD()); List<int> listint = new List<int> { 0,1,2,3,4,5,6,7,8,9,10}; var queryint = listint.MyWhere(x=>x%2==0);//取出偶数 foreach (var item in queryint) { Console.WriteLine(item); } Console.WriteLine("==============扩展方法地盘结束=============");
目前Program.cs中的代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using linq_Ch1.Entity; namespace linq_Ch1 { class Program { //初始化一些委托 public delegate int AddOne(int i); public delegate int ToAdd(int i,int j); public delegate void Method1(); static void Main(string[] args) { //1 对象初始化器 2013年4月1日22:14:52 List<Student> students = new List<Student>(); students.Add(new Student { Id = 1, StudentName = "达一", Birthday = DateTime.Now }); students.Add(new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }); students.Add(new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now }); Console.WriteLine("用对象初始化器方式:" + students[1].StudentName); //2 集合初始化器 2013年4月1日22:19:06 List<Student> students_2 = new List<Student> { new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}, new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }, new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now } }; Console.WriteLine("用集合初始化器方式:" + students[1].StudentName); //Dictionary版本 Dictionary<string, Student> student_dic = new Dictionary<string, Student> { {"达一",new Student { Id=1,StudentName="达一",Birthday=DateTime.Now}}, {"坤二", new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }}, {"张三",new Student { Id=3,StudentName="张三",Birthday=DateTime.Now}}, }; Console.WriteLine("用集合初始化器方式Dictionary版本:" + student_dic["达一"].StudentName); //3.使用var关键字,就是不显示声明的类型 var var_int = 1; var var_double = 2.22; var var_string = "茗洋芳竹"; Console.WriteLine("var_int是数字吗?"+(var_int is int)); Console.WriteLine("var_double是浮点型数字吗?" + (var_double is double)); Console.WriteLine("var_string是字符串吗?" + (var_string is string)); //4.使用var保存查询Student的结果 List<Student> stu = new List<Student>(); for (int i = 1; i < 10; i++) { stu.Add(new Student { Id=i,StudentName="学生"+i.ToString(),Birthday=DateTime.Now}); } var query = from o in stu where o.Id < 5 select new { 学生编号 = o.Id, 学生姓名 = o.StudentName }; foreach (var item in query) { Console.WriteLine(item.学生编号+":"+item.学生姓名); } //5创建隐型数组 var arr1 = new[] {0,1,2,3,4,5,6,7,8,9,10 }; var arr2 = new[,] { {"1","小A","xa"},{"2","小B","xb"},{"3","小C","xc"}}; Console.WriteLine(arr1[1]); //1 Console.WriteLine(arr2[1,1]); //小B //6.创建匿名类型的对象 var StudentInfo = new { Id=1,StudentName="陈四",Birthday=DateTime.Now}; Console.WriteLine("生日是:"+StudentInfo.Birthday.ToString("yyyy年MM月dd日")); //7 lambda AddOne myAddOne; //参数的多少跟委托的签名的参数相等 myAddOne = x => x + 1;//隐式声明一个参数,表达式方法体 myAddOne = (int x) => x + 1;//显示声明一个参数,表达式方法体 ToAdd myToAdd = (x, y) => x + y; //lambda表达式支持用表达式或语句块作为方法体,语法上比匿名方法更加灵活(匿名方法的方法体只能是语句块) Method1 myMethod=()=>Console.WriteLine("myToAdd"); //无参数,表达式方法体 myMethod(); //调用myMethod方法 //8.用语句作为Lambda表达式的方法体 myAddOne = x => { return x + 1; }; myAddOne = (int x) => { return x + 1; }; Console.WriteLine("正确答案应该是5:,你的答案是"+myAddOne(4)); //9 扩展方法 Console.WriteLine("==============扩展方法地盘============="); Console.WriteLine(DateTime.Now.ToYYYYMMDD()); List<int> listint = new List<int> { 0,1,2,3,4,5,6,7,8,9,10}; var queryint = listint.MyWhere(x=>x%2==0);//取出偶数 foreach (var item in queryint) { Console.WriteLine(item); } Console.WriteLine("==============扩展方法地盘结束============="); Console.ReadLine(); } } //扩展方法 static class ExtendMethod{ public static string ToYYYYMMDD(this DateTime dt)//自身必须是静态方法,第一个参数必须是this,后面紧跟要扩展的类的名称 { return dt.ToString("yyyyMMdd"); } public static IEnumerable<TSource> MyWhere<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { foreach (TSource s in source) { if(predicate(s)) yield return s; } } } }
2 LINQ TO Objects查询
2.1 简单查询
说明:一个班级有多个学生,在Entity文件夹下添加ClassRoom.cs文件,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace linq_Ch1.Entity { public class ClassRoom { public int ClassId { get; set; } public string ClassName { get; set; } List<Student> students = new List<Student>(); } }
在Program.cs中添加一个方法,用于初始化查询数据
static List<ClassRoom> BuildClassRoom() { List<ClassRoom> classroom = new List<ClassRoom>(); for (int j = 1; j < 4; j++) //创建3个班级 { List<Student> stu = new List<Student>(); for (int i = 1; i < 10; i++) { stu.Add(new Student { Id = i, StudentName = j + "班的学生" + i, Birthday = DateTime.Now }); } classroom.Add(new ClassRoom { ClassId = j, ClassName = j + "班", students = stu }); } return classroom; }
在void Main方法中添加如下代码开始查询
//初始化数据 List<ClassRoom> classinfo = BuildClassRoom(); //查出2班内学生编号小于5的学生信息 var classQuery = from c in classinfo from s in c.students where c.ClassId == 2 && s.Id < 5 select new { 学生ID = s.Id, 学生姓名 = s.StudentName, 学生班级 = c.ClassName }; foreach (var item in classQuery) { Console.WriteLine("学生ID="+item.学生ID+",姓名="+item.学生姓名+",所在班级="+item.学生班级); } Console.ReadLine(); }
2.2 应用自定义方法筛选数据
①在Program类下添加一个方法
static bool IsXingWang(string name) { return name.IndexOf("王") > -1; }
②使用方法
//用自定义的方法筛选数据 List<Student> students_defined = new List<Student>(); students_defined.Add(new Student { Id = 1, StudentName = "王一", Birthday = DateTime.Now }); students_defined.Add(new Student { Id = 2, StudentName = "坤二", Birthday = DateTime.Now }); students_defined.Add(new Student { Id = 3, StudentName = "张三", Birthday = DateTime.Now }); //找出含有 王 字的学生信息 var definedFilter = from s in students_defined where IsXingWang(s.StudentName) select s;
string fmt = "学生编号={0};姓名={1}"; foreach (var items in definedFilter) { Console.WriteLine(string.Format(fmt,items.Id,items.StudentName)); }
2.3 将字符串数组按照元素的长度分组(练习group by)
代码如下
//练习group by string[] words =new string[]{"What","is","your","name","?","my","name","is","mingyang","."};
var Groups = from word in words group word by word.Length into lengthGroups orderby lengthGroups.Key descending select new { Length = lengthGroups.Key, WordCollection = lengthGroups }; //遍历每组的单词 foreach (var group in Groups) { Console.WriteLine("包含"+group.Length.ToString()+"个字符的单词有"); foreach (string word in group.WordCollection) { Console.Write("\t" + word); } Console.WriteLine(); }
按照字符的长度分组后,每一组看成一个整体,我们把每一组的值通过into关键字保存到临时变量lengthGroups里,那么这个临时变量可以看做是一个整体,里面的内容大致如下
第二种方法:
//第二种方法 var twogroup = words.GroupBy(word=>word.Length); foreach (IGrouping<int,string> group in twogroup) { Console.WriteLine("包含"+group.Key.ToString()+"个字符的单词有"); foreach (string word in group) { Console.Write("\t" + word); } Console.WriteLine(); }
2.4 获得序列中元素的索引位置
类似MSSQL数据库ROW_NUMBER()函数的功能
代码如下
//获得序列中元素的索引位置 List<Student> studentsIndex = new List<Student>(); studentsIndex.Add(new Student { Id = 101, StudentName = "达一", Birthday = DateTime.Now }); studentsIndex.Add(new Student { Id = 102, StudentName = "坤二", Birthday = DateTime.Now }); studentsIndex.Add(new Student { Id = 103, StudentName = "张三", Birthday = DateTime.Now }); var index_string_query = studentsIndex.Select((person, index) => new { index, person.StudentName}) .OrderBy(i=>i.StudentName); foreach (var item in index_string_query) { Console.WriteLine(item.index+":"+item.StudentName); }
2.5 Orderby (多条件排序)
方式1:
//Orderby //获得序列中元素的索引位置 List<Student> studentsOrderby= new List<Student>(); studentsOrderby.Add(new Student { Id = 104, StudentName = "达一", Birthday = Convert.ToDateTime("1991-04-01") }); studentsOrderby.Add(new Student { Id = 102, StudentName = "战三", Birthday = Convert.ToDateTime("1991-04-04") }); studentsOrderby.Add(new Student { Id = 103, StudentName = "坤二", Birthday = Convert.ToDateTime("1991-02-22") }); //集合带的 var orderQuery1 = studentsOrderby.OrderBy(p => p.Birthday).ThenBy(p=>p.StudentName); //多条件排序 foreach (var item in orderQuery1) { Console.WriteLine(item.Id+" "+item.StudentName+" "+item.Birthday.ToYYYYMMDD()); }
方式2:
var orderQuery2 = from o in studentsOrderby orderby o.Birthday, o.Id select o; foreach (var item in orderQuery2) { Console.WriteLine(item.Id + " " + item.StudentName + " " + item.Birthday.ToYYYYMMDD()); }
2.6 Reverse,Enumerable类的Reverse方法,反转序列中的元素
Console.WriteLine("反转后的studentsOrderby"); studentsOrderby.Reverse(); foreach (var item in studentsOrderby) { Console.WriteLine(item.Id + " " + item.StudentName + " " + item.Birthday.ToYYYYMMDD()); }
2.7 自定义ForEach操作符
首先添加一个在ExtendMethod类添加一个扩展方法
//ForEach,每次遍历序列的时候,都将值交给一个自定义的方法去操作和处理 public static void ForEach<T>(this IEnumerable<T> source,Action<T> func) { foreach (var item in source) { func(item); } }
然后在 void main方法中添加代码
Console.WriteLine("自定义扩展ForEach"); //ForEach List<Student> studentsForEach= new List<Student>(); studentsForEach.Add(new Student { Id = 104, StudentName = "达一", Birthday = Convert.ToDateTime("1991-04-01") }); studentsForEach.Add(new Student { Id = 102, StudentName = "战三", Birthday = Convert.ToDateTime("1991-04-04") }); studentsForEach.Add(new Student { Id = 103, StudentName = "坤二", Birthday = Convert.ToDateTime("1991-02-22") });
studentsForEach.ForEach(p => { Console.WriteLine(p.Id+" "+p.StudentName); });
2.8 限定符操作
①All
集合.All(lambda表达式)操作,示例代码如下
//限定符操作 //All 判断studentsForEach中的所有学生的生日是不是大于>90年的 bool result=studentsForEach.All(x=>x.Birthday>Convert.ToDateTime("1990-12-31")); Console.WriteLine(result); //True
用Count判断
bool result2 = studentsForEach.Count(x => x.Birthday > Convert.ToDateTime("1990-12-31")) > studentsForEach.Count(); Console.WriteLine(result); //True
②Any
跟All比较一下,就知道了,我们把All换成Any,意思就是,在studentsForEach集合中是否存在至少一个学生大于90年的
用集合.Exists(…..) 也可以实现Any效果
③Contains最常用的,集合.Contains(值),检查寻列中时候包含指定元素
2.9 元素操作
①ElementAt(索引)获得指定位置的元素
List<int> ints = new List<int> { 1,2,3,4,5,6}; int intresult = ints.ElementAt(3); //4 获取指定位置的元素,从0开始 Console.WriteLine(intresult);
使用ints.Skip(3).First();也可以达到同样的效果
②集合.First() ;获取第一个元素,同样 ElementAt(0)也可以达到同样的效果
③集合.Last() ;获取最后一个元素
④集合.Where(Lambda表达式),条件筛选出数据。例如 StudentsList.Where(x=>x.Id==1);找出Id等于1的学生对象
⑤集合.OrderbyDescending(Lambda表达式),按什么条件降序,Orderby是升序
⑥集合.Single(Lambda表达式)方法返回源序列中满足指定条件唯一元素,如果有多个这样的元素存在,则会引发异常
⑦集合.SingleOrDefault(Lambda表达式)方法返回源序列中满足指定条件唯一元素,如果有多个这样的元素存在,则会引发异常.如果该序列为空,则返回默认值。
⑧集合.DefaultIfEmpty()如果源序列为空,返回只包含一个元素(值为默认值)的序列;如果是集合.DefaultIfEmpty(参数)的
例如int型的数组ints,代码为 ints.DefaultIfEmpty(-1),则代表如果ints序列为空,则返回-1
⑨创建一个空序列 例如 var query=Enumerable.Empty<Student>等同于代码 Student[] s=new Student[0];
⑩创建一个指定范围值的数组
例如: int[] ints=Enumerable.Range(0,10).ToArray();//则数组有10个,分别是0,1,2,3,4,5…,9
List<int> ints=Enumerable.Range(0,10).ToList();
(11).创建一个重复值的数组
string[] strings2 = Enumerable.Repeat("9", 3).ToArray();
strings2.ForEach(p => { Console.WriteLine(p); });
则创建一个数组,等同于 string[] s=new string[]{"9","9","9"};
关于还剩一点linq to objects明天写