这里只是贴一些代码示例,很容易看懂,忘记了怎么用了就来翻看一下。
目录
1.不同的查询方法
using System;
using System.Linq;
namespace TestLinq_1
{
class MainClass
{
public static void Main(string[] args)
{
int[] numbers = { 2, 3, 5, 23, 32, 12, 45, 2 };
var numsQuery = from x in numbers
where x < 20
select x;
var numsMethod = numbers.Where(x => x < 20);
int numsCount = (from x in numbers
where x < 20
select x).Count();
foreach (var x in numsQuery)
Console.Write("{0},", x);
Console.WriteLine();
foreach (var x in numsMethod)
Console.Write("{0},",x);
Console.WriteLine();
Console.WriteLine(numsCount);
}
}
}
2.查询表达式的结构
查询表达式由查询休后的from子句组成,有关查询表达式需要了解的一些重要事项如下:
-
子句必须按照一定的顺序出现
-
from子句和select..group子句这两部分是必须的
-
其它子句是可选的
-
在LINQ查询表达式中,select子句在表达式的最后,这与SQL的select语句在查询的开始处不一样。C#这么做的原因是让Visual Studio只能感应我们在输入代码时给我们更多选项
-
可以有任意多的from...let...where子句
2.1. from子句
from子句指定了要作为数据源使用的数据集合,它还引入迭代变量。迭代变量逐个表示数据源的每一个元素。
from子句的语法如下:
from Type item in Items
int [] arr={1,2,3,56,32};
var query= from item in arr
where item <13
select item ;
2.2. join子句
LINQ中的join子句和SQL中的join子句很相似。如果你熟悉SQL中的联结,那么LINQ中的联结对你来说也应该很容易理解。使用联结结合两个或更多集合中的数据;联结操作接受两个集合然后创建一个李恩施对象集合,每一个对象包含原始集合对象中的所有字段。
联结的语法如下:它指定了第二个集要和之前子句中的集合进行联结。注意必须使用上下文关键字equals来比较字段,不能用== 运算符。
join Identifier in Collection2 on Field1 equals Field2
举个例子
var query from s in students
join c in studentsInCourse on s.StID equals c.StId
2.3 from和join的完整示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GenMethod
{
class Program
{
public class Student
{
public int stuID;
public string LastName;
}
public class CourseStudent
{
public string CourseName;
public int stuID;
}
static Student[] students = new Student[]
{
new Student{ stuID=1,LastName="张三" },
new Student{stuID=2,LastName="李四"},
new Student{stuID=3,LastName="王五"},
new Student{stuID=4,LastName="孔乙己"}
};
static CourseStudent[] courseStudents = new CourseStudent[]
{
new CourseStudent{ CourseName="art",stuID=1},
new CourseStudent{CourseName="art",stuID=2},
new CourseStudent{CourseName="history",stuID=1},
new CourseStudent{CourseName="history",stuID=3},
new CourseStudent{CourseName="history",stuID=4},
new CourseStudent{CourseName="physic",stuID=2},
};
static void Main(string[] args)
{
//找出所有选了历史课的学生的姓
var query = from s in students
join c in courseStudents on s.stuID equals c.stuID
where c.CourseName == "history"
select s.LastName;
foreach (var q in query)
Console.WriteLine("students takeing history: {0}", q);
}
}
}
结果如下:
2.3 from...let ...where
可选的from...let..where部分是查询主题的第一部分,可以有任意数量的3个子句来组合——from子句、let子句和where子句。主体本身可以从任何数量的其它from子句开始,每一个from子句都指定了一个额外的元数据集合,并引入了要在之后运算的迭代变量,所有的from子句语法和含义都是一样的。
下面给出了一个示例:
-
第一个from子句是查询表达式必须的子句
-
第二个from子句是第一个子句的查询主体
-
select子句创建了一个类型的对象
static void Main()
{
var groupA=new[] {3,4,5,6};
var groupB=new[] {6,7,8,9};
var someInts=from a in groupA //必须的第一个from子句
from b in groupB //查询主体的第一个子句
where a>4 && b<=8
select new {a,b ,sum=a+b}; //匿名类型对象
foreach(var a in someInts)
{
Console.WriteLine(a);
}
}
let子句接受一个表达书的运算并且它把它赋值给一个需要在其他运算中的使用的标识符,let子句的语法如下:
let Identifier =Expression // 类似于一个赋值语句
所以将上面的查询语句修改为:
var someInts = from a in groupA //必须的第一个from子句
from b in groupB //查询主体的第一个子句
let sum=a+b //在新的变量中保存结果
where a > 4 && b <= 8
select new { a, b, sum = a + b }; //匿名类型对象
会得到和上面一样的结果。
2.4 where子句
where子句根据之后的运算来出去不符合指定条件的项,语法如下:
where BooleanExpression
注意事项:
- 只要是在from...let...where部分中,查询表达式可以有任意多个where子句
- 一个像必须满足where子句才能避免在之后被过滤
对于上一小节中的查询语句修改为:
var someInts = from a in groupA //必须的第一个from子句
from b in groupB //查询主体的第一个子句
let sum =a+b
where a ==4
where sum>=11
select new { a, b, sum = a + b }; //匿名类型对象
会得到所有sum大于11,且a等于4的结果。
2.5 orderby子句
orderby子句接受一个表达式并根据表达式暗顺序返回结果项。orderby子句默认排序是升序,然而也可以使用ascending和descending关键字显示地设置元素的排序方式;可以有任意多个子句,他们必须使用都好分隔。语法如下:
orderby Expression {ascending/ descending}
表达式通常是项的一个字段,该字段不一定非得是数值字段,也可以是字符串这样的可排序类型。
using System;
using System.Linq;
namespace TestLinq2
{
public class Student
{
public string LName;
public string FName;
public int Age;
public string Major;
}
class MainClass
{
public static void Main(string[] args)
{
Student[] students = //创建匿名类型对象
{
new Student {LName="Jones",FName="Mary",Age=19,Major="history"},
new Student {LName="Simith",FName="Bob",Age=20,Major="CompSci"},
new Student {LName="Lina",FName="Carol",Age=18,Major="history"}
};
var query = from s in students
orderby s.Age
select s;
foreach (var q in query)
Console.WriteLine("{0},{1},{2},{3}", q.LName, q.FName, q.Age, q.Major);
}
}
}
2.6 group子句
group子句把select的对象根据一些标准进行分组,例如有了之前的学生数组,可以根据他们的主修课程进行分组。使用group自居需要注意以下几点:
- 如果项包含在查询的结果中,它们就可以根据某字段的值进行分组。作为分组一句的属性叫做健
- group子句返回的不是原始数据源中项的枚举,而是返回可以枚举已经形成的项的分组的可枚举类型
- 分组本身时可枚举类型,它们可以枚举实际的项。
namespace TestLinq2
{
public class Student
{
public string LName;
public string FName;
public int Age;
public string Major;
}
class MainClass
{
public static void Main(string[] args)
{
Student[] students = //创建匿名类型对象
{
new Student {LName="Jones",FName="Mary",Age=19,Major="history"},
new Student {LName="Simith",FName="Bob",Age=20,Major="CompSci"},
new Student {LName="Lina",FName="Carol",Age=18,Major="history"}
};
var query = from s in students
orderby s.Age
group s by s.Major;
foreach (var q in query)
{
Console.WriteLine("{0}", q.Key);
foreach(var t in q)
Console.WriteLine(" {0},{1},{2},{3}", t.LName, t.FName, t.Age, t.Major);
}
}
}
}
代码结果如下:
分析示意图如下:
2.7 into子句
into语句可以将查询到结果作为一个集合继续在结果中查询。查询延续语法如下:
举个简单的例子就很容易懂了。
using System;
using System.Linq;
namespace Test_into
{
class MainClass
{
public static void Main(string[] args)
{
var A = new[] { 1, 2, 3, 4, 5 ,45,12};
var B = new[] { 2, 3, 4, 5, 6, 32, 45, 123 };
var query=from a in A
join b in B on a equals b
into AandB //查询延续
from c in AandB
select c;
foreach (var x in query)
Console.Write("{0} ", x);
}
}
}
输出结果: 2 3 4 5 45
3. 标准查询运算符
标准查询运算符由一系列API方法组成,它能让我们查询任何.net数组或集合。注意事项如下:
- 被查询的集合对象叫序列,它必须实现IEnumerate<T> 接口,T是类型
- 标准查询运算符使用方法语法
- 一些运算符范围IEnumerable对象,一些运算符返回标量
- 很多操作符都以一个谓词作为参数,谓词是一个方法,它以对象作为参数,根据对象是否满足某个条件而返回true或false
- 用作方法的运算符直接作用于序列对象,在这里就是numbers数组。
- 返回类型不是IEnumerable对象,而是int。
class Program
{
static int[] numbers =new int [] {2,4,6,8};
static void main(string[] args)
{
int total=numbers.Sum();
int howMany=numbers.Count();
Console.WriteLine("Total: {0},Count: {1}",total, howMany);
}
}
下面是一些应该熟悉的标准查询运算符
Where | 根据给定的谓词对序列进行过滤 |
Select | 指定要包含一个对象或对象的一部分 |
SelectMany | 一种查询类型,返回集合的集合。该方法将这些结果合并为一个集合 |
Take | 接受一个输入参数count,返回序列中前count个对象 |
Skip | 接受一个参数count,跳过序列中的前count个对象 |
TakeWhile | 接受一个谓词,开始迭代序列,只要谓词对当前计算结果返回true,就选择该项,在谓词返回第一个false的时候,该项和其余项都被丢弃 |
SkipWhile | 接受一个谓词,开始迭代序列,只要谓词对当前计算结果返回true,就跳过该项,在谓词返回第一个false的时候,该项和其余项都被选择 |
Join | 对两个序列执行内联结, |
GroupJoin | 可以产生层级结果的链接,第一个序列的各个元素和第二个序列中的元素集合相关联 |
Concat | 联结两个序列 |
OrderBy/ThenBy | 根据一个活多喝健对序列中的元素排序 |
Reverse | 反转序列中的元素 |
GroupBy | 分组序列中的元素 |
Distinct | 去除序列中的重复项 |
Union | 返回两个序列的并集 |
Intersect | 返回两个序列的交集 |
Except | 操作两个序列,返回第一个序列中不重复的元素减去第二个序列中的元素 |
AsEnumrable | 将序列作为IEnumerable<TSource>返回 |
ToArray | 将序列作为数组返回 |
Tolist | 将序列作为List<T>返回 |
ToDictionary | 将序列作为Dictionary<Tkey,Telement> |
ToLookUp | 将序列作为LookUp<Tkey,Telement> |
ofType | 将返回序列中的元素是指定的类型 |
Cast | 将序列中所有元素强制转换为给定的类型 |
SequenceEqual | 返回一个布尔值,指定两个序列是否相等 |
First | 返回序列中第一个与谓词匹配的元素,如果没有就抛出一个异常 |
Last | 返回序列中最后一个与谓词匹配的元素,如果没有就返回默认值 |
Single | 返回序列中与谓词匹配的单个元素,如果没有元素匹配,或多余一个元素匹配就抛出异常 |
SingleOrDefault | 返回序列中与谓词匹配的单个元素,如果没有元素匹配,或多余一个元素匹配就返回默认值 |
ElementAt | 给定一个参数n,返回序列中的第n+1个元素 |
ElementAtOrDefault | 给定一个参数n,返回序列中的第n+1个元素,如果索引超出范围,就返回默认值 |
DefaultIfEmpty | 提供一个在序列为空的默认值 |
Range | 给定一个start整型和count整型,返回range(start,start+count+1) ,类似于python中的range |
Repeat | 给定一个T类型的elemnet和count整数,返回一个序列具有count个element的副本 |
Empty | 返回一个给定类型T的空序列 |
Any | 返回一个布尔值,指定序列中是否含有给定谓词的元素 |
All | 返回一个布尔值,指定序列中的元素是符都满足谓词 |
Contains | 返回一个布尔值,指定序列中是否包含给定的元素 |
Count | 返回序列中元素的个数,(int) |
LongCount | 返回序列中元素的个数,(long) |
Sum | 序列求和 |
Min | 返回序列的最小值 |
Max | 返回序列的最大值 |
Average | 返回序列的平均值 |
Aggregate | 连续对序列中的各个元素应用给定的函数 |
4. 使用委托参数
LINQ定义了两套泛型委托类型,与标准查询运算符一起使用,即Func委托和Action委托,各有17个成员。我们用做实参的委托对象必须是这些类型或者类型之一,TR代表返回值,并且总是在类型参数列表的最后一个。
这里列出前四个泛型Func委托:
public delegate TR Func<out TR> ();
public delegate TR Func<in T1, out TR> (T1 a1);
public delegate TR Func<in T1, in T2, out TR> (T1 a1,T2, a2);
public delegate TR Func<in T1, in T2, in T3, out TR> (T1 a1,T2, a2,T3 a3);
注意返回类型有一个out关键字,使之可以协变,也就是可以接受声明的类型,或者从这个类型派生的类型。同理,输入参数有一个in关键字,使之可以逆变,也就是你可以接受声明的类型或这个类型的派生类型。
如下是4个action委托,与Func委托类似,只是没有返回值,因此也就没有返回值的类型参数
public delegate void Action ();
public delegate void Action<in T1> (T1 a1);
public delegate void Action<in T1, in T2> (T1 a1,T2, a2);
public delegate void Action<in T1, in T2, in T3> (T1 a1,T2, a2,T3 a3);
使用委托的示例:
using System;
using System.Linq;
namespace TestDelegateLinq
{
class MainClass
{
static bool IsOdd(int x)
{
return x % 2 == 1;
}
public static void Main(string[] args)
{
int[] intArray = { 3, 4, 5, 6, 7, 8, 9 };
Func<int, bool> myDel = new Func<int, bool>(IsOdd);
var countOdd=intArray.Count(myDel);
Console.WriteLine("count of odd numbers: {0}",countOdd);
}
}
}
这里解释一下count运算符被重载有两种形式,一种是
public static int Count<T>(this IEnumerable<T> source),它有一个参数,返回集合元素的个数。而要返回元素中奇数的个数,则需要第二种形式:
public static int Count<T>(this IEnumerable<T> source ,Func<T,bool> predicate),这一种方法使用时必须提供一个接受单个T类型的输入参数并返回bool值的委托对象。
当然对于简单的委托函数,可以用lambda表达式实现更容易,比如实现上面相同的功能,可以这么写:
var countOdd=intArray.Count(n=> n%2==1);