由于个人在学校没有接触过Linq,而且在工作上运用Linq的时候也比较多,准备把LINQ的相关知识学习整理梳理一遍,希望能填补下这个知识点,也为未来减轻压力。
LINQ查询表达式使用C#常见的语言构造,从外观上看,和我们常用的SQL类似,并且查询表达式中的变量可以用匿名类型,所以在很多情况下,不需要指定变量类型就可以构建LINQ表达式。
LINQ的数据源可以是数据库对象或是XML流等,也可以使实现了IEnumerable或者泛型IEnumberable<T>接口的集合对象。
LINQ的基本语法包含如下的8个上下文关键字,如下:
关键字 | 说明 |
from | 指定范围变量和数据源 |
where | 根据bool表达式从数据源中筛选数据 |
select | 指定查询结果中的元素所具有的类型或表现形式 |
group | 对查询结果按照键值进行分组(IGrouping<TKey,TElement>) |
into | 提供一个标识符,它可以充当对join、group或select子句结果的引用 |
order | 对查询出的元素进行排序(ascending/descending) |
join | 按照两个指定匹配条件来Equals连接两个数据源 |
let | 产生一个用于存储查询表达式中的子表达式查询结果的范围变量 |
1、From子句:
如果要写一个LINQ表达式,就必须是以from子句开头。
Person person = new Person();
Student student = new Student();
//判断指定的两个成员是否存在继承关系(判断一个类是否可用赋值给另一个类) --后者继承于前者
bool b1 = typeof(Person).IsAssignableFrom(typeof(Student)); //student继承了person
bool b3 = person.GetType().IsAssignableFrom(student.GetType());//另一种写法
bool b2 = typeof(Itest).IsAssignableFrom(typeof(Teacher));
//判断是否是指定类的实例
bool b4 = typeof(Person).IsInstanceOfType(student); //结果为true student继承了person
bool b5 = person.GetType().IsInstanceOfType(student);//另一种写法 GetType当前对象的实例
//判断是否是某个类的子类,非接口
bool b6 = typeof(Student).IsSubclassOf(typeof(Person));
//判断是否是抽象
if (typeof(Drive).IsAbstract)
{
Console.WriteLine("是抽象的");
}
else
{
Console.WriteLine("不是抽象的");
}
Console.ReadKey();
//复合from子句
List<Person> personList2 = new List<Person>()
{
new Person{ Name = "张三", Age = 20, Phone = new List<string>(){"666666","138******"}},
new Person{ Name = "李四", Age = 21, Phone = new List<string>(){"777777","138******"}},
new Person{ Name = "王五", Age = 22, Phone = new List<string>(){"888888","138******"}}
};
//person、phone都是查询变量,作用域为当前查询语句
var new_personList2 = from person in personList2
from phone in person.Phone
where phone.IndexOf("6666") > -1 //查询个人电话集合以"6666"开头的人
select person;
foreach (var new_person in new_personList2)
{
Console.WriteLine("{0} 年龄{1}", new_person.Name, new_person.Age);
foreach (var person_phone in new_person.Phone)
{
Console.WriteLine("电话:{0}", person_phone);
}
}
Console.ReadKey();
//4多个from子句
var personList3 = from Person person in personList
where person.Age > 21 //查询集合1年龄超过21的人
from Person person2 in personList2
where person2.Age == 21 //查询集合2年龄等于21的人
select new { person, person2 };//查询结果定制
foreach (var new_person in personList3)
{
Console.WriteLine("{0} {1}", new_person.person.Name, new_person.person2.Name);
}
Console.ReadKey();
运行结果如下:
2、Where子句:
where子句是LINQ表达式的元素筛选机制,除了开始和结束的位置,它几乎可以出现在LINQ表达式的任意位置上。在一个LINQ表达式中,可以有where子句,也可以没有;可以有一个,也可以有多个;多个where子句之间的逻辑关系相当于逻辑“与”,每个where子句可以包含1个或多个bool逻辑表达式,这些条件成为谓词,谓词逻辑之间用的是“&&”或“||”等而不是SQL中的and 、or。
//自定义函数
static bool Check(string name)
{
if (name.Substring(0, 1) == "李")
return false;
return true;
}
运行结果如下:
3、Select子句:
在select子句上可以非常灵活的处理查询到的元素,然后再把结果返回。
//常见的where语句
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张三三", Age = 20, Phone = "555555"},
new Person(){ Name = "李四", Age = 21, Phone = "666666"},
new Person(){ Name = "王五", Age = 22, Phone = "777777"}
};
var new_personList = from person in personList
where (person.Name.Length == 3 || person.Name.Substring(0, 1) == "李") && person.Age > 20 //查询姓名长度为3或姓名以"李"开头且年龄大于20的人
select new { person.Name, person.Age };
foreach (var new_person in new_personList)
{
Console.WriteLine("{0},{1}", new_person.Name, new_person.Age);
}
Console.ReadKey();
//在where子句中使用自定义函数
var new_personList2 = from Person person in personList
where person.Name.Length == 2
&& Check(person.Name) //查询姓名长度为2且符合自定义函数限定后的人
select person;
foreach (var new_person in new_personList2)
{
Console.WriteLine("{0},{1},{2}", new_person.Name, new_person.Age, new_person.Phone);
}
Console.ReadKey();
//动态谓词的筛选
//定义动态谓词数组,在实际开发中可以动态获得
string[] names = { "李四", "XXX", "***", "@@@", "一些敏感词" };
var new_personList3 = from Person person in personList
where !names.Contains(person.Name) //查询姓名不包含集合元素的人
select person;
foreach (var new_person in new_personList3)
{
Console.WriteLine("{0} 年龄:{1},电话:{2}", new_person.Name, new_person.Age, new_person.Phone);
}
Console.ReadKey();
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张三", Age = 20, Phone = "555555"},
new Person(){ Name = "李四", Age = 21, Phone = "666666"},
new Person(){ Name = "王五", Age = 22, Phone = "777777"}
};
//常见的select语句
var new_personList = from person in personList
where person.Age >= 22 && person.Age <= 30 //查询年龄大于等于22小于等于30的人
select new { person.Name, person.Age }; //处理查询结果,返回查到的人的姓名和年龄
foreach (var new_person in new_personList)
{
Console.WriteLine(new_person.Name + “ 年龄:” + new_person.Age);
}
Console.ReadKey();
//对查询到的结果集进行替换
var new_personList2 = from person in personList
where person.Age >= 21 && person.Age <= 30 //查询年龄大于等于22小于等于30的人
select person.Name.Replace("李", "王"); //处理查询结果,返回查到的人的姓名替换后的结果
foreach (var new_person in new_personList2)
{
Console.WriteLine(new_person);
}
Console.ReadKey();
//在select子句中使用自定义函数
var new_personList3 = from person in personList
where person.Age >= 20 && person.Age <= 21 //查询年龄大于等于20小于等于21的人
select MyDesc(person.Name); //处理查询结果,返回自定义函数处理后的人的姓名
foreach (var new_person in new_personList3)
{
Console.WriteLine(new_person);
}
Console.ReadKey();
//对查询结果进行投影
var new_personList4 = from person in personList
where person.Age >= 21 && person.Age <= 22 //查询年龄大于等于21小于等于22的人
select new NewPerson { Name = "我的名字叫:" + person.Name, Age = person.Age + 1 }; //处理查询结果,返回投影结果
foreach (var new_person in new_personList4)
{
Console.WriteLine(new_person.Name + new_person.Age);
}
Console.ReadKey();
static string MyDesc(string s)
{
return s + "好帅";
}
public class NewPerson
{
public string Name { get; set; }
public int Age { get; set; }
}
运行结果如下:
4、Group子句:
LINQ表达式必须以from子句开头,以select或group子句结束,所以除了使用select来返回结果外,也可以使用group子句来返回元素分组后的结果。group子句返回的是一个基于IGrouping<TKey,TElement>泛型接口的对象序列。
注意:语法和SQL的group有点区别。
运行结果如下:
5、Into子句:
into子句作为一个临时标识符,用于group、select、join子句中充当其结果的引用。
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张一", Age = 20, Phone = "333333"},
new Person(){ Name = "张二", Age = 21, Phone = "444444"},
new Person(){ Name = "张三", Age = 22, Phone = "555555"},
new Person(){ Name = "李四", Age = 23, Phone = "666666"},
new Person(){ Name = "李五", Age = 24, Phone = "777777"},
new Person(){ Name = "赵六", Age = 25, Phone = "888888"},
new Person(){ Name = "田七", Age = 26, Phone = "999999"},
};
var new_personList = from person in personList
group person by person.Name.Substring(0, 1); //按姓氏进行分组
//遍历键值和键值所属元素
foreach (IGrouping<string, Person> new_person in new_personList)
{
Console.WriteLine();
Console.WriteLine("姓:{0} ", new_person.Key);
foreach (var p in new_person)
{
Console.WriteLine("{0} 年龄:{1} 电话:{2}", p.Name, p.Age, p.Phone);
}
}
Console.ReadKey();
Console.WriteLine();
Console.WriteLine("-----------------------------------");
var new_personList2 = from person in personList
group person by person.Age > 23; //按年龄是否大于23进行分组
foreach (IGrouping<bool, Person> new_person in new_personList2)
{
Console.WriteLine();
Console.WriteLine("年龄 {0} 23 ", new_person.Key ? "大于" : "小于");
foreach (var p in new_person)
{
Console.WriteLine("{0} 年龄:{1} 电话:{2}", p.Name, p.Age, p.Phone);
}
}
Console.ReadKey();
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张一", Age = 20, Phone = "333333"},
new Person(){ Name = "张二", Age = 21, Phone = "444444"},
new Person(){ Name = "张三", Age = 12, Phone = "555555"},
new Person(){ Name = "李四", Age = 23, Phone = "666666"},
new Person(){ Name = "李五", Age = 14, Phone = "777777"},
new Person(){ Name = "赵六", Age = 25, Phone = "888888"},
new Person(){ Name = "田七", Age = 16, Phone = "999999"},
};
//into用于group子句
var new_personList = from person in personList
group person by person.Name.Substring(0, 1) into person_group //相当于分组后的组名
select person_group;
foreach (var persontGroup in new_personList)
{
Console.WriteLine("姓:{0} ", persontGroup.Key);
foreach (var p in persontGroup)
{
Console.WriteLine("{0} 电话:{1}", p.Name, p.Phone);
}
}
Console.ReadKey();
Console.WriteLine();
//select子句中的into子句
var new_personList2 = from person in personList
select new { NewName = person.Name, NewAge = person.Age } into newperson //相当于创建一个新的对象
orderby newperson.NewAge
select newperson;
foreach (var persontGroup in new_personList2)
{
Console.WriteLine("{0} 年龄:{1}", persontGroup.NewName, persontGroup.NewAge);
}
Console.ReadKey();
运行结果如下:
6、OrderBy子句:
按照元素的一个或多个属性对元素进行排序。
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张一", Age = 20, Phone = "333333"},
new Person(){ Name = "张二二", Age = 20, Phone = "444444"},
new Person(){ Name = "张三", Age = 12, Phone = "555555"},
new Person(){ Name = "李四", Age = 23, Phone = "666666"},
new Person(){ Name = "李五", Age = 14, Phone = "777777"},
new Person(){ Name = "赵六", Age = 25, Phone = "888888"},
new Person(){ Name = "田七", Age = 16, Phone = "999999"},
};
//按照年龄排序
var new_personList = from person in personList
orderby person.Age //默认升序ascending,如果要降序要加上descending
select person;
foreach (var new_person in new_personList)
{
Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone);
}
Console.ReadKey();
Console.WriteLine();
//按照年龄进行降序排序,按照名字字数进行次要排序
var new_personList2 = from person in personList
orderby person.Age descending, person.Name.Length descending
select person;
foreach (var new_person in new_personList2)
{
Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone);
}
Console.ReadKey();
运行结果如下:
7、Let子句:
let子句用于在LINQ表达式中存储子表达式的计算结果。
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张一", Age = 20, Phone = "333333"},
new Person(){ Name = "张二二", Age = 20, Phone = "444444"},
new Person(){ Name = "张三", Age = 12, Phone = "555555"},
new Person(){ Name = "李四", Age = 23, Phone = "666666"},
new Person(){ Name = "李五", Age = 14, Phone = "777777"},
new Person(){ Name = "赵六", Age = 25, Phone = "888888"},
new Person(){ Name = "田七", Age = 16, Phone = "999999"},
};
//使用let子句创建范围变量g,并通过g构建查询表达式
var new_personList = from person in personList
let g = person.Name.Substring(0, 1)
where g == "张" || g == "李"
select person;
foreach (var new_person in new_personList)
{
Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone);
}
Console.ReadKey();
Console.WriteLine();
//也可以不使用let,上面的语句等效于下
var new_personList2 = from person in personList
where person.Name.Substring(0, 1) == "张" || person.Name.Substring(0, 1) == "李"
select person;
foreach (var new_person in new_personList2)
{
Console.WriteLine("{0} 年龄:{1} 电话:{2}", new_person.Name, new_person.Age, new_person.Phone);
}
Console.ReadKey();
运行结果如下:
8、Join子句:
join子句能将两个数据源中元素可以进行相等比较的两个属性进行关联。使用equals关键字进行相等比较,而不是常用的双等号。
//定义两个数据源
List<Person> personList = new List<Person>()
{
new Person(){ Name = "张三", Age = 12, Phone = "555555"},
new Person(){ Name = "李四", Age = 23, Phone = "666666"},
new Person(){ Name = "王五", Age = 14, Phone = "777777"},
new Person(){ Name = "赵六", Age = 25, Phone = "888888"},
new Person(){ Name = "田七", Age = 16, Phone = "999999"},
};
List<PersonFile> personFileList = new List<PersonFile>()
{
new PersonFile(){ Name = "张三", File = "三好学生"},
new PersonFile(){ Name = "李四", File = "班长"},
new PersonFile(){ Name = "王五", File = "学习委员"},
new PersonFile(){ Name = "赵六", File = "差生"}
};
//根据姓名进行内连接
var new_personList = from person in personList
join file in personFileList on person.Name equals file.Name
select new { Name = person.Name, file = file.File, Age = person.Age };
foreach (var new_person in new_personList)
{
Console.WriteLine("{0} {1} 年龄:{2}", new_person.Name, new_person.file, new_person.Age);
}
Console.ReadKey();
Console.WriteLine();
//等效于前面的多个from的效果
var new_personList2 = from person in personList
from file in personFileList
where person.Name == file.Name
select new { Name = person.Name, File = file.File, Age = person.Age };
foreach (var new_person in new_personList2)
{
Console.WriteLine("{0} {1} 年龄:{2}", new_person.Name, new_person.File, new_person.Age);
}
Console.ReadKey();
Console.WriteLine();
//根据姓名进行分组连接
var new_personList3 = from person in personList
join file in personFileList on person.Name equals file.Name into person_group
select new { Name = person.Name, Files = person_group };
foreach (var new_person in new_personList3)
{
Console.WriteLine(new_person.Name);
foreach (var p in new_person.Files)
{
Console.WriteLine(p.File);
}
}
Console.ReadKey();
Console.WriteLine();
//根据姓名进行左外连接
var new_personList4 = from person in personList
join file in personFileList on person.Name equals file.Name into person_group
from position in person_group.DefaultIfEmpty()
select new { Name = person.Name, File = position == null ? "无" : position.File };
foreach (var new_person in new_personList4)
{
Console.WriteLine("{0} {1} ", new_person.Name, new_person.File);
}
Console.ReadKey();
运行结果如下:
对Linq的基本子句用法学习和总结完毕,还有待继续练习达到灵活运行!