19.1 LINQ provider
LINQ数据源可以是SQL数据库、数组、XML文档,每一种数据源类型,都有根据该数据源实现查询的代码模块,叫做LINQ provider。有LINQ to objects、LINQ to xml、LINQ to SQL、LINQ to Datasets、LINQ to Entities。
//一个简单的LINQ例子
int[] nums = { 2,13,4,15};//数据源
IEnumerable<int> lowNums = from n in nums where n < 10 select n;//定义并存储查询返回一个枚举类
foreach (var x in lowNums)
{
Console.WriteLine("{0},", x);
}
19.2 匿名类型
匿名类型的对象创建表达式:
匿名类型没有名字,必须使用var关键字来作为变量类型
//匿名类型
var student = new { Name = "Mary Jones", Age = 19, Major = "History" };//必须使用var {}内为匿名对象初始化语句
Console.WriteLine("{0},{1},{2}",student.Name,student.Age,student.Major);
string major = "History";
var student1 = new { Other.name, Age = 19, major };//分别是赋值形式成员访问表达式形式和标识符形式
Console.WriteLine("{0},{1},{2}", student.Name, student.Age, student.Major);
19.2 方法语法和查询表达式
在写LINQ查询的实收可以使用两种形式的语法:查询和方法
//方法语法与查询语法
int[] nums1 = { 2, 5, 26, 62, 45, 77, 74 };
var numsQuery = from n in nums1 where n < 20 select n;//查询语法
var numsMethod = nums1.Where(x => x < 20);//方法语法
int numsCount=(from n in nums1 where n<20 select n).Count();//两种形式的组合
19.3 查询变量
查询变量就是LINQ查询返回的值,有枚举类的IEnumerable<T>和标量int。
如果查询表达式返回枚举,查询一直到处理枚举时才执行,而如果返回标量,则立即执行,并把结果保存在查询变量中。
19.4 查询表达式的结构
顺序:from……select……这两部分是必需的;select子句在最后面
from子句
join子句
var query=from s in a join c in b on a.ID= equals b.ID
let子句
where子句
例子
var groupA=new[]{3,4,5,6};
var groupB=new[]{6,7,8,9};
var someInts = fromint a in groupA//必需的第一个from子句
fromint b in groupB//第二个from子句
let sum = a + b//let子句在新的变量中保存结果
where sum >= 11 //where条件1
where a == 4//where条件2
selectnew { a, b, sum };
orderby子句
查询中的匿名类型
var students = new[]{
new {LName="Jones",FName="Mary",Age=19,Major="History"},
new {LName="Smith",FName="Bob",Age=20,Major="CompSci"},
new {LName="Fleming",FName="Carol",Age=21,Major="Math"}
};//定义一个匿名类型的对象数组每个匿名类型的对应属性名字必需相同
var query = from s in students selectnew { s.LName, s.FName, s.Major };
//在select子句中创建一个匿名类型
group by子句
var query = from s in students
group s by s.Major;
foreach (var s in query)
{
Console.WriteLine("{0}",s.Key);//分组键
foreach(var t in s)
{ Console.WriteLine(" {0},{1}", t.LName, t.FName); }
}
into子句
var groupA=new[]{3,4,5,6};
var groupB=new[]{6,7,8,9};
var someInts1 = fromint a in groupA
join b in groupB on a equals b
into groupAB//将前面的查询结果插入到新的groupAB中
from c in groupAB
select c;
19.5 标准查询运算符
标准运算符就是一系列的API方法,能够查询任何.NET数组和集合。
对于标准查询运算符,被查询的集合对象叫做序列。
序列:它必须实现IEnumerable<T>接口,包括List<>、Dictionary<>、Stack<>、Array等。
标准查询运算符:Where Select SelectMane Take Skip TakeWhile SkipWhile Join GroupJoin Concat ……Count Sum Contains All Any Max Last First Range FirstOrDefalut…
19.6.1标准查询运算符的签名
System.Linq.Enumerable类声明了标准查询运算符方法,这些方法不仅仅是一些方法,它们是扩展了IEnumerable<T>泛型类的扩展方法,IEnumerable<T>就是实现了接口IEnumerable的所有类类型。例子如下:
int[] array = newint[] { 3,4,5,6,7,9};
var c1 = Enumerable.Count(array);
var f1 = Enumerable.First(array);//方法语法
var c2 = array.Count();
//扩展语法 int数组继承自Array类 该类实现了IENumerable<T>接口 所以可以是被扩展对象
var f2 = array.First();
Count方法签名
publicstaticint Count<TSource>(thisIEnumerable<TSource> source);
扩展方法是公共的静态方法,尽管定义在一个类中,但目的是为了另一个类(第一个形参)增加功能,所以叫扩展方法。
19.6.2 将委托作为参数
委托可以理解为:一个包含特殊签名和返回类型的方法或方法列表的对象,当委托被调用时,包含它的方法会被依次调用。
Count方法还有一种重载形式:
public static int Count<TSource>(thisIEnumerable<TSource> source, Func<TSource, bool> predicate);
其中第二个参数就是泛型委托参数。Func<TSource, bool> predicate:接受单个T类型的参数作为方法参数并且返回一个bool类型的值。这种委托成为谓词。
LINQ预定义委托:Func委托和Action委托,各有17个成员。我们用作实参的委托对象必须是这些类型之一。Fuc委托有返回类型,而Action没有。
Func委托:
委托的几种定义方式:方法、匿名方法、Lambda表达式
Func<string, string, string> func1 = Hello;
// Func委托,是微软为我们预定义的常用委托前面的T参数都表示的是方法的输入参数最后一个TResult参数是方法返回结果参数
Func<string, string, string> func2 = delegate(string a, string b) { return"func2" + Environment.NewLine + a + " " + b; };
//匿名方法 delegate+(参数列表)+{方法体}
Func<string, string, string> func3 = (a, b) => { return"func3" + Environment.NewLine + a + " " + b; };
//Lambda表达式 (,,) => { } 左边为参数列表右边为表达式体
匿名对象
var person = new { name = "yuanxiaoxia", age = 27 };//定义了一个匿名对象person,有两个属性string和int类型
例子:--使用委托对象来调用标准查询运算符方法Count
static bool IsOdd(int x)
{
return x % 2 == 1;
}
static void Main()
{
int[] array = new int[] { 3, 4, 5, 6, 7, 9 };
Func<int, bool> myDel = newFunc<int, bool>(IsOdd);
var countOdd = array.Count(myDel);
var countOdd1 = Enumerable.Count(array,myDel);//两种调用委托的方式
}
燕青解答:
Count是一个扩展方法,是IEnumerable<TSourse>泛型的一个扩展方法,它接受一个委托类型的参数:Func<Tsource,bool>
Lambda表达式就是一个方法的简写
委托是一个数据类型,实例化一个委托对象时 把具有委托定义的方法签名的方法 做为参数传入,所以这个方法可以是任何形式,可以传入一个Lambda表达式也可以是一个普通的方法,或是匿名方法
还可以使用更简洁的方法,Lambda表达式来作为参数
例子:--使用Lambda表达式来调用标准查询运算符方法Count
var countOdd2 = array.Count(x => x % 2 == 1);
也可以使用匿名方法
例子:--使用匿名方法来调用标准查询运算符方法Count
Func<int, bool> Del = delegate(int x) { return x % 2 == 1; };//匿名方法:delegate+(参数列表)+{方法体}
var countOdd3 = array.Count(Del);
19.6 LINQ to XML
LINQ与xml可以通过两种方式配合使用:第一种是xml操作API;第二种是使用LINQ查询工具
19.6.1 API:
1、创建、保存、加载和显示xml文档
XDocument employees1 = newXDocument(newXElement("Employees",newXElement("Name","Bob"),newXElement("NAme","Sally")));
employees1.Save("EmployeesFile.xml");
XDocument employees2 = XDocument.Load("EmployeesFile.xml");
Console.WriteLine(employees2);
2、获取xml树的值
XDocument employeeDoc = newXDocument(
newXElement("Employees", newXElement("Employee", newXElement("Name", "Bob"), newXElement("PhoneNum", "13468654108")),
newXElement("Employee",newXElement("Name","Sally"),newXElement("PhoneNum","13468654102"),newXElement("PhoneNum","13468654101"))));
XElement root = employeeDoc.Element("Employees");
IEnumerable<XElement> employees=root.Elements();
foreach (XElement emp in employees)
{
XElement empNameNode = emp.Element("Name");
Console.WriteLine(empNameNode.Value);
IEnumerable<XElement> empPhones = emp.Elements("PhoneNum");
foreach (XElement empPhone in empPhones)
{
Console.WriteLine(" {0}", empPhone.Value);
}
}
![](https://images2015.cnblogs.com/blog/328547/201706/328547-20170608162721278-648770403.png)
3、增加节点以及操作xml
XDocument xd = newXDocument(
newXElement("root", newXElement("first"))
);
Console.WriteLine("Original tree");
Console.WriteLine(xd);
XElement rt = xd.Element("root");
rt.Add(newXElement("Second"));
rt.Add(newXElement("Third"), newXComment("Important Comment"), newXElement("Fourth"));
Console.WriteLine("Modified tree");
Console.WriteLine(xd);
![](https://images2015.cnblogs.com/blog/328547/201706/328547-20170608162804168-1341393598.png)
4、xml特性(XAttribute)
XDocument xd = newXDocument(
newXElement("root",
newXAttribute("color","red"),//特性构造函数,接受两个参数name和value
newXAttribute("size","large"),
newXElement("first"),
newXElement("Second"))
); //创建xml树
Console.WriteLine(xd);
XElement rt = xd.Element("root");//获取元素
XAttribute color = rt.Attribute("color");//获取节点特性
Console.WriteLine("color is {0}",color.Value);//读取特性的value值
rt.Attribute("color").Remove();//移除节点的一个color特性
rt.SetAttributeValue("size", null);//size特性赋值为空,其实也就是移除节点的一个size特性
rt.SetAttributeValue("size", "medium");//改变特性值
rt.SetAttributeValue("width", "narrow");//添加特性width
Console.WriteLine(xd);
5、节点的其他类型
(1)XComment 注释 <!--内容-->
(2)XDeclaration xml声明
(3)XProcessingInstruction
XDocument xd = newXDocument(
newXDeclaration("1.0", "utf-8", "yes"),
newXDocument("This is a comment"),
newXProcessingInstruction("xml-stylesheet", @"href=""stories.css"" type=""test/css"""),
newXElement("root",
newXElement("first"),
newXElement("Second")
)
);
结果:
19.6.2 LINQ查询
使用LINQ查询来得到节点的子集
XDocument xd = newXDocument(
newXElement("MyElements",
newXElement("first",
newXAttribute("color", "red"),
newXAttribute("size", "small")),
newXElement("second",
newXAttribute("color", "red"),
newXAttribute("size", "medium")),
newXElement("third",
newXAttribute("color", "blue"),
newXAttribute("size", "large")))
);
Console.WriteLine(xd);
xd.Save("SimpleSample.xml");
xd = XDocument.Load("SimpleSample.xml");//加载文档
XElement rt = xd.Element("MyElements");//获取根元素
var xyz = from e in rt.Elements()
where e.Name.ToString().Length == 5
select e;//选择名称包含5个字符的元素
foreach (XElement x in xyz)
Console.WriteLine(x.Name.ToString());//所选的元素
Console.WriteLine();
foreach (XElement x in xyz)
Console.WriteLine("Name:{0},color:{1},size:{2}",x.Name,x.Attribute("color").Value,x.Attribute("size").Value);
var xyz1 = from e in rt.Elements()
selectnew { e.Name,color=e.Attribute("color")};//创建匿名类型
foreach (var x in xyz1)
Console.WriteLine(x);
结果: