一、揭开linq的神秘面纱
(一)概述
  LINQ的全称是Language Integrated Query,中文译成“语言集成查询”。LINQ作为一种查询技术,首先要解决数据源的封装,大致使用了三大组件来实现这个封装,分别是LINQ to Object、LINQ to ADO.NET、LINQ to XML。它们和.NET语言的关系如下

要使用LINQ来编程,首先要学习使用LINQ的子句以及由查询语法构成的查询表达式。C#3.0和VB9开始将这种查询语法引入到了编程语言,并新增了一系列的关键字。但对于CLR本身来说,它并不了解查询语法,它能理解的是由编程语言的编译器将这种查询语法转换成的方法。这些方法叫“标准查询运算符”,它们具有类似这样的名—Where、Select、GroupBy、Join。下面就以C#为例,从编程语言的层面来具体介绍这些查询语法(注意VB9也支持这种查询语法)。

LINQ的查询由3基本部分组成:获取数据源,创建查询,执行查询

     // 1,获取数据源
List<int> numbers = new List<int>() { , , , , , , , , , }; // 2,创建查询
var numQuery = from num in numbers
where num % ==
select num;
// 3,执行查询
foreach (var num in numQuery)
{
Console.WriteLine("{0,1}", num);
}

下图显示了完整的查询操作。在 LINQ 中,查询的执行与查询本身截然不同;换句话说,如果只是创建查询变量,则不会检索任何数据。

如上例所示,Linq的数据源要求必须实现IEnumerable或IEnumerable<T>接口,数组隐式支持这个接口。numQuery叫做查询变量,它存储了一个查询表达式。注意,声明查询变量并不会执行查询,真正的执行查询延迟到了foreach语句中。

linq的机制用到的详细知识点请参考:https://www.cnblogs.com/liulun/archive/2013/02/26/2909985.html

二、linq常用方法小试牛刀

1. from子句

创建一个LINQ表达式必须要以from子句开头。

1.1 单个from子句

            string[] values = { "中国", "日本", "美国", "菲律宾", "越南" };

            //查询包含“国”的字符串
var valueQuery = from v in values
where v.IndexOf("国") > 0
select v; foreach (var v in valueQuery)
{
Console.WriteLine("{0,1}", v);
}

在这个LINQ表达式的from子句中,v叫做范围变量,values是数据源。v的作用域存在于当前的LINQ表达式,表达式以外不能访问这个变量。where用来筛选元素,select用于输出元素。这里的范围变量v,和foreach语句中得隐式变量v都可以由编译器推断出其类型。
运行的结果如下:

中国
美国

使用LINQ查询List<T>集合

        public class CustomerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public string Tel { get; set; }
}
private void formExpDemo2()
{
//这里用了,对象和集合初始化器
List<CustomerInfo> customers = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="诸葛菲菲", Age=23, Tel ="1380524****"}
};
//查询年龄大于20的客户,注意这里的范围变量用了显示类型CustomerInfo
var query = from CustomerInfo ci in customers
where ci.Age > 20
select ci; foreach (CustomerInfo ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
}

结果:

姓名:欧阳晓晓 年龄:35 电话:1330708****
姓名:诸葛菲菲 年龄:23 电话:1380524****

1.2 复合from子句

在查询数据源中,元素的属性是一个集合时,可以使用复合from子句对这个属性集合查询。比如,一个客户,可能有多个电话。

        public class CustomerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public List<string> TelTable { get; set; }
}
private void formExpDemo()
{
List<CustomerInfo> customers = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, TelTable=new List<string>{"1330708****","1330709****"}},
new CustomerInfo{ Name="上官飘飘", Age=17, TelTable=new List<string>{"1592842****","1592843****"}},
new CustomerInfo{ Name="诸葛菲菲", Age=23, TelTable=new List<string>{"1380524****","1380525****"}}
};
//查询包含电话号码1592842****的客户
var query = from CustomerInfo ci in customers
from tel in ci.TelTable
where tel.IndexOf("1592842****") > -1
select ci; foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1}", ci.Name, ci.Age);
foreach (var tel in ci.TelTable)
{
Console.WriteLine(" 电话:{0}", tel);
}
}
}

结果:

姓名:上官飘飘 年龄:17
电话:1592842****
电话:1592843****

1.3 多个from子句

多个from子句查询和复合from子句从字面上看似乎一样,其实是不同的操作。复合from子句查询的是单个数据源中的子元素的集合,而多个from子句,是载入多个数据源进行查询。

        private void formExpDemo()
{
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="诸葛菲菲", Age=23, Tel ="1380524****"}
};
List<CustomerInfo> clist2 = new List<CustomerInfo> {
new CustomerInfo{ Name="令狐冲", Age=25, Tel ="1330708****"},
new CustomerInfo{ Name="东方不败", Age=35, Tel ="1592842****"},
new CustomerInfo{ Name="任盈盈", Age=23, Tel ="1380524****"}
}; //在clist中查找Age大于20的客户,
//在clist2中查找Age小于30的客户
var query = from customer in clist
where customer.Age > 20
from customer2 in clist2
where customer2.Age < 30
select new { customer, customer2 }; foreach (var ci in query)
{
Console.WriteLine("{0} {1}", ci.customer.Name,ci.customer2.Name);
}
}

在select语句中,我们用了匿名类型来存储筛选出的元素,这样得到的完全是一个交叉联接表,有点类似于SQL中的笛卡尔乘积。

输出的结果:

欧阳晓晓 令狐冲
欧阳晓晓 任盈盈
诸葛菲菲 令狐冲
诸葛菲菲 任盈盈

2、where子句

where子句的作用就是筛选元素,除了开始和结束位置,where子句几乎可以出现在LINQ表达式的任意位置。一个LINQ表达式中可以有where子句,也可以没有;可以有一个,可以有多个;多个where子句之间的关系相当于逻辑“与”,每个where子句可以包含1个或多个逻辑表达式,这些条件成为“谓词”,多个谓词之间用布尔运算符隔开,比如逻辑“与”用&&,逻辑“或”用||,而不是用SQL中的AND或OR。

2.1 常见的where子句查询

            List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="令狐冲", Age=23, Tel ="1380524****"}
}; //查询名字是3个字或者姓“令”的,但年龄大于20的客户
var query = from customer in clist
where (customer.Name.Length == 3 || customer.Name.Substring(0, 1) == "令")
&& customer.Age > 20
select customer; foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}

结果:

姓名:令狐冲 年龄:23 电话:1380524****

2.2 在where子句中使用自定义函数

        private void whereExpDemo()
{
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="令狐冲", Age=23, Tel ="1380524****"}
}; //查询名字是3个字并且姓“令”的客户
var query = from customer in clist
where (customer.Name.Length == 3 && CheckName(customer.Name))
select customer; foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
}
private bool CheckName(string name)
{
if (name.Substring(0, 1) == "令")
return true;
else
return false;
}

结果:

姓名:令狐冲 年龄:23 电话:1380524****

 2.3 动态谓词的筛选

上面的几个例子都是给定了查询谓词然后进行查询,有时候谓词的数量可能并不固定,是随情况变化的。例如:一组名字可能是运行时动态指定的。

            List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="令狐冲", Age=23, Tel ="1380524****"}
}; //定义动态的谓词数组,这个数组应该由实际运行环境生成
string[] names = { "令狐冲", "任盈盈", "杨过", "小龙女", "欧阳晓晓" }; //查询在给定谓词数组里存在的客户
var query = from customer in clist
where names.Contains(customer.Name)
select customer; foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}

结果:

姓名:欧阳晓晓 年龄:35 电话:1330708****
姓名:令狐冲 年龄:23 电话:1380524****

3. select子句

LINQ表达式的结果是使用select子句获得的。select子句可以对数据进行转换,这个过程称为“投影”。select子句产生的类容,取决于前面的所有子句及其自身表达式执行后的结果。

3.1 输出查询结果

最简单的select就是直接输出from子句建立的那个范围变量:

            var query = from customer in clist
where names.Contains(customer.Name)
select customer;

也可以输出范围变量类型中得某个属性:

                        select customer.Name;

或者修改一下再输出:

select customer.Name.Replace("gg","mm");

或者干脆使用一个自定义的函数,把范围变量传进去,输出处理后的结果:

select MyFunction(customer.Name);

3.2 对查询结果进行投影

        public class MyCustomerInfo
{
public string Name { get; set; }
public string Tel { get; set; }
}
private void whereExpDemo()
{
List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="令狐冲", Age=23, Tel ="1380524****"}
}; //定义动态的谓词数组,这个数组应该由实际运行环境生成
string[] names = { "令狐冲", "任盈盈", "杨过", "小龙女", "欧阳晓晓" }; //查询在给定谓词数组里存在的客户
var query = from customer in clist
where customer.Age < 30
select new MyCustomerInfo { Name = customer.Name, Tel = customer.Tel }; foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 电话:{1} 类型{2}", ci.Name, ci.Tel,ci.GetType().FullName);
}
}

上例中,在select子句中用对象初始化器生成了新的数据类型,从而进行了数据转换,使元素变成了MyCustomerInfo类型。

姓名:上官飘飘 电话:1592842**** 类型LinqDemo.Form1+MyCustomerInfo
姓名:令狐冲 电话:1380524**** 类型LinqDemo.Form1+MyCustomerInfo

4. group子句

按照语法的规定,LINQ表达式必须以from子句开头,以select或group子句结束,所以除了使用select子句外,也可以使用guoup子句来返回元素分组后的结果。group子句返回的是一个IGrouping<TKey,TElement>泛型接口的对象集合,下面先了解下这个接口。

4.1 IGrouping<TKey,TElement>泛型接口

这个接口表示具有公共键的对象集合,它的原型如下:

public interface IGrouping<TKey, TElement> : IEnumerable<TElement>,
IEnumerable

TKey是键的对象类型,在用于group子句的时候,数据类型会有编译器推断出来,它一般用于存储分组的键值;TElement是指的对象类型,用于存储分组的结果,变量基于这个接口的类型就是遍历这个值。

4.2 分组查询

分组查询对于关系型数据库是非常常见的一种操作,但在没有LINQ之前,对内存的对象进行分组却是一件非常麻烦的事情。现在,在LINQ表达式中只需要使用group子句就可以轻松完成对内存对象的分组。

            List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="欧阳锦鹏", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官无忌", Age=23, Tel ="1380524****"}
}; //按照名字的前2个字进行分组
var query = from customer in clist
group customer by customer.Name.Substring(0, 2); foreach (IGrouping<string,CustomerInfo> group in query)
{
Console.WriteLine("分组键:{0}",group.Key);
foreach (var ci in group)
{
Console.WriteLine("姓名:{0} 电话:{1}", ci.Name, ci.Tel);
}
Console.WriteLine("***************************************");
}

上例代码,按照form子句建立的范围变量customer的Name属性的前两个字作为键值进行分组。所以TKey的类型是一个字符串类型。

分组键:欧阳
姓名:欧阳晓晓 电话:1330708****
姓名:欧阳锦鹏 电话:1330708****
***************************************
分组键:上官
姓名:上官飘飘 电话:1592842****
姓名:上官无忌 电话:1380524****
***************************************

再看一个分组的例子:

            //按照年龄是否大于20分组
var query = from customer in clist
group customer by customer.Age > 20; foreach (var group in query)
{
Console.WriteLine("分组键:{0}",group.Key);
foreach (var ci in group)
{
Console.WriteLine("姓名:{0} 电话:{1}", ci.Name, ci.Tel);
}
Console.WriteLine("***************************************");
}

group子句用了一个布尔表达式,所以IGrouping<TKey,TElement>的TKey变成了一个bool型。并且循环遍历的时候可以用var代替IGrouping的声明:

foreach (var group in query)
分组键:True
姓名:欧阳晓晓 电话:1330708****
姓名:欧阳锦鹏 电话:1330708****
姓名:上官无忌 电话:1380524****
***************************************
分组键:False
姓名:上官飘飘 电话:1592842****
***************************************

5. into子句

into子句作为一个临时标识符,用于select,group,join子句中。

  List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="欧阳锦鹏", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官无忌", Age=23, Tel ="1380524****"}
}; //按照名字的前两个字进行分组,再用分组Key进行排序
var query = from customer in clist
group customer by customer.Name.Substring(0, 2) into gpcustomer
orderby gpcustomer.Key descending
select gpcustomer;
Console.WriteLine("into 用于group子句");
foreach (var group in query)
{
Console.WriteLine("分组键:{0}", group.Key);
foreach (var ci in group)
{
Console.WriteLine("姓名:{0} 电话:{1}", ci.Name, ci.Tel);
}
Console.WriteLine("***************************************");
} var query2 = from customer in clist
select new { NewName = customer.Name, NewAge = customer.Age } into newCustomer
orderby newCustomer.NewAge
select newCustomer; Console.WriteLine("into 用于select子句");
foreach (var ci in query2)
{
Console.WriteLine("{0} 年龄:{1}", ci.NewName, ci.NewAge);
}

into子句提供了一个临时标识符,它存储了into子句前面的查询内容,使它后面的子句可以方便的使用,对其进行再次查询,投影等操作。
执行结果:

into 用于group子句
分组键:上官
姓名:上官飘飘 电话:1592842****
姓名:上官无忌 电话:1380524****
***************************************
分组键:欧阳
姓名:欧阳晓晓 电话:1330708****
姓名:欧阳锦鹏 电话:1330708****
***************************************
into 用于select子句
上官飘飘 年龄:17
上官无忌 年龄:23
欧阳晓晓 年龄:35
欧阳锦鹏 年龄:

6. 排序子句

LINQ可以按元素的一个或多个属性对元素进行排序。LINQ表达式的排序方式分为OrderBy、OrderByDescending、ThenBy、ThenByDescending这四种。

6.1 OrderBy和OrderByDescending

OrderBy用于按元素的值进行升序,语法:

orderby 用于排序的元素的表达式

OrderByDescending用于按元素的值进行降序,语法:

orderby 用于排序的元素的表达式 descending
            List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="欧阳锦鹏", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官无忌", Age=23, Tel ="1380524****"}
}; //按照年龄升序
var query = from customer in clist
orderby customer.Age
select customer;
Console.WriteLine("按年龄升序排列");
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
//按照年龄降序
var query2 = from customer in clist
orderby customer.Age descending
select customer;
Console.WriteLine("\n按年龄降序排列");
foreach (var ci in query2)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}

运行结果:

按年龄升序排列
姓名:上官飘飘 年龄:17 电话:1592842****
姓名:上官无忌 年龄:23 电话:1380524****
姓名:欧阳晓晓 年龄:35 电话:1330708****
姓名:欧阳锦鹏 年龄:35 电话:1330708**** 按年龄降序排列
姓名:欧阳晓晓 年龄:35 电话:1330708****
姓名:欧阳锦鹏 年龄:35 电话:1330708****
姓名:上官无忌 年龄:23 电话:1380524****
姓名:上官飘飘 年龄:17 电话:1592842****

6.2 ThenBy和ThenByDescending

ThenBy和ThenByDescending用于对元素进行次要排序。基本语法:

orderby 用于排序的元素表达式,用于排序的元素表达式
orderby 用于排序的元素表达式,用于排序的元素表达式 descending
            List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="郭靖", Age=17, Tel ="1330708****"},
new CustomerInfo{ Name="黄蓉", Age=17, Tel ="1300524****"}
}; //按照年龄升序,再按名字的字数次要排序
var query = from customer in clist
orderby customer.Age,customer.Name.Length
select customer;
Console.WriteLine("按年龄排列,按名字字数进行次要排序");
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
//按照年龄升序,再按名字的字数降序次要排序
var query2 = from customer in clist
orderby customer.Age, customer.Name.Length descending
select customer;
Console.WriteLine("\n按年龄排列,按名字字数进行降序次要排序");
foreach (var ci in query2)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}
//按照年龄升序,再按名字的字数降序要排序,在按电话号码进行第三条件排序
var query3 = from customer in clist
orderby customer.Age, customer.Name.Length,customer.Tel
select customer;
Console.WriteLine("\n按年龄,名字字数,电话号码排序");
foreach (var ci in query3)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}

执行结果:

按年龄排列,按名字字数进行次要排序
姓名:郭靖 年龄:17 电话:1330708****
姓名:黄蓉 年龄:17 电话:1300524****
姓名:上官飘飘 年龄:17 电话:1592842****
姓名:欧阳晓晓 年龄:35 电话:1330708**** 按年龄排列,按名字字数进行降序次要排序
姓名:上官飘飘 年龄:17 电话:1592842****
姓名:郭靖 年龄:17 电话:1330708****
姓名:黄蓉 年龄:17 电话:1300524****
姓名:欧阳晓晓 年龄:35 电话:1330708**** 按年龄,名字字数,电话号码排序
姓名:黄蓉 年龄:17 电话:1300524****
姓名:郭靖 年龄:17 电话:1330708****
姓名:上官飘飘 年龄:17 电话:1592842****
姓名:欧阳晓晓 年龄:35 电话:1330708****

 7. let子句

let子句用于在LINQ表达式中存储子表达式的计算结果。let子句创建一个范围变量来存储结果,变量被创建后,不能修改或把其他表达式的结果重新赋值给它。此范围变量可以再后续的LINQ子句中使用。

            List<CustomerInfo> clist = new List<CustomerInfo> {
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="郭靖", Age=17, Tel ="1330708****"},
new CustomerInfo{ Name="黄蓉", Age=17, Tel ="1300524****"}
}; //姓“郭”或“黄”的客户
var query = from customer in clist
let g = customer.Name.Substring(0,1)
where g == "郭" || g == "黄"
select customer;
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} 电话:{2}", ci.Name, ci.Age, ci.Tel);
}

使用let 建立了个范围变量,这个范围变量在后续的where子句中使用,如果不使用let子句,where子句的表达式将写成这样:

where customer.Name.Substring(0, 1) == "郭" || customer.Name.Substring(0, 1) == "黄"
姓名:郭靖 年龄:17 电话:1330708****
姓名:黄蓉 年龄:17 电话:1300524****

8. join子句

如果一个数据源中元素的某个属性可以跟另一个数据源中元素的属性进行相等比较,那么这两个数据源可以用join子句进行关联。jion子句用equals关键字进行比较,而不是常见的==。

      List<CustomerInfo> clist = new List<CustomerInfo>
{
new CustomerInfo{ Name="欧阳晓晓", Age=35, Tel ="1330708****"},
new CustomerInfo{ Name="上官飘飘", Age=17, Tel ="1592842****"},
new CustomerInfo{ Name="郭靖", Age=17, Tel ="1330708****"},
new CustomerInfo{ Name="黄蓉", Age=17, Tel ="1300524****"}
}; List<CustomerTitle> titleList = new List<CustomerTitle>
{
new CustomerTitle{ Name="欧阳晓晓", Title="歌手"},
new CustomerTitle{ Name="郭靖", Title="大侠"},
new CustomerTitle{ Name="郭靖", Title="洪七公徒弟"},
new CustomerTitle{ Name="黄蓉", Title="才女"},
new CustomerTitle{ Name="黄蓉", Title="丐帮帮主"}
}; //根据姓名进行内部联接
Console.WriteLine("内部联接");
var query = from customer in clist
join title in titleList
on customer.Name equals title.Name
select new { Name = customer.Name, Age = customer.Age, Title = title.Title };
foreach (var ci in query)
{
Console.WriteLine("姓名:{0} 年龄:{1} {2}", ci.Name, ci.Age, ci.Title);
}
//根据姓名进行分组联接
Console.WriteLine("\n根据姓名进行分组联接");
var query2 = from customer in clist
join title in titleList
on customer.Name equals title.Name into tgroup
select new { Name = customer.Name, Titles = tgroup };
foreach (var g in query2)
{
Console.WriteLine(g.Name);
foreach (var g2 in g.Titles)
{
Console.WriteLine(" {0}", g2.Title);
}
}
//根据姓名进行 左外部联接
Console.WriteLine("\n左外部联接");
var query3 = from customer in clist
join title in titleList
on customer.Name equals title.Name into tgroup
from subTitle in tgroup.DefaultIfEmpty()
select new { Name = customer.Name, Title = (subTitle == null ? "空缺" : subTitle.Title) };
foreach (var ci in query3)
{
Console.WriteLine("姓名:{0} {1} ", ci.Name, ci.Title);
}

要仔细理解上例的,内联接,分组联接,以及左联接。

内部联接
姓名:欧阳晓晓 年龄: 歌手
姓名:郭靖 年龄: 大侠
姓名:郭靖 年龄: 洪七公徒弟
姓名:黄蓉 年龄: 才女
姓名:黄蓉 年龄: 丐帮帮主 根据姓名进行分组联接
欧阳晓晓
歌手
上官飘飘
郭靖
大侠
洪七公徒弟
黄蓉
才女
丐帮帮主 左外部联接
姓名:欧阳晓晓 歌手
姓名:上官飘飘 空缺
姓名:郭靖 大侠
姓名:郭靖 洪七公徒弟
姓名:黄蓉 才女
姓名:黄蓉 丐帮帮主

三、Linq to Objects之延期执行方法

LINQ to Objects是 LINQ的基础,而 LINQ to SQL、 LINQ to XML是中间 LINQ提供程序,他们主要是把数据源转换成 LINQ to Objects兼容的类型,以便 LINQ to Objects进行操作。 LINQ to Objects就是直接对IEnumerable或泛型IEnumerable<T>集合进行查询。LINQ表达式是LINQ标准查询运算符的一部分,而LINQ标准查询运算符则是LINQ to Objects的基础。它们是一组静态方法,被定义在System.Linq.Enumerable和System.Linq.Queryable类中。这两个类中方法基本一致,唯一的不同点是System.Linq.Queryable类中方法会把LINQ表达式拆解成表达式目录树,其他一些Linq提供程序可以将这个表达式目录树翻译成查询语句,比如SQL语句,然后再执行相关操作。

本文主要学习System.Linq.Enumerable的扩展方法,这些方法按照执行的行为不同,可以分为延期执行和立即执行。延期执行的运算符在枚举时被执行,下面要学习的就是延期执行方法的一部分。

1,Take 方法

Take方法用于从一个序列的开头返回指定数量的元素

            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };

            //直接输出前3个元素
Console.WriteLine("Take方法直接输出前3个元素");
foreach (var name in names.Take(3))
{
Console.WriteLine(name);
}
var query = from n in names
where n.Length == 2
select n;
Console.WriteLine("\nTake方法输出查询结果的前1个元素");
foreach (var s in query.Take(1))
{
Console.WriteLine(s);
}

输出结果:

Take方法直接输出前3个元素
郭靖
李莫愁
欧阳晓晓 Take方法输出查询结果的前1个元素
郭靖
 

2,TakeWhile 方法

TakeWhile方法获取序列中从开头起符合条件的元素,直到遇到不符合条件的元素为止的所有元素。条件代理部分有两种形式:

Func<TSource, bool> predicate

Func<TSource, int, bool> predicate 第二个参数是元素的索引

注意:当条件为假时,就停止了,后面的元素不会输出。

            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };

            //输出名字小于4个字的元素
var takeNames = names.TakeWhile(n => n.Length < 4);
foreach (var name in takeNames)
{
Console.WriteLine(name);
}
Console.WriteLine("\nTakeWhile 带索引参数");
//输出名字字数小于等于4 并且索引小于4的元素
foreach (var name in names.TakeWhile((n, i) => n.Length <= 4 && i < 4))
{
Console.WriteLine(name);
}

输出结果:

郭靖
李莫愁 TakeWhile 带索引参数
郭靖
李莫愁
欧阳晓晓
黄蓉

3,Skip 方法

   Skip方法用于跳过序列中指定个数的元素。

            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };

            //跳过前3个元素
Console.WriteLine("Take方法跳过前3个元素");
foreach (var name in names.Skip(3))
{
Console.WriteLine(name);
}
var query = from n in names
where n.Length == 2
select n;
Console.WriteLine("\nTake方法跳过查询结果的前1个元素");
foreach (var s in query.Skip(1))
{
Console.WriteLine(s);
}

输出结果:

Take方法跳过前3个元素
黄蓉
黄药师 Take方法跳过查询结果的前1个元素
黄蓉

4,SkipWhile 方法

SkipWhile 方法用于只要满足指定的条件,就跳过序列中得元素。

注意当遇到条件为假时,就停止跳越了输出剩余的所有元素

 
            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };

            Console.WriteLine("SkipWhile跳过名字为2个字的元素");
foreach (var name in names.SkipWhile(n => n.Length == 2))
{
Console.WriteLine(name);
} Console.WriteLine("\nSkipWhile跳过名字小于4个字,并且索引小于2");
foreach (var s in names.SkipWhile((n, i) => n.Length < 4 && i < 2))
{
Console.WriteLine(s);
}

输出结果:

SkipWhile跳过名字为2个字的元素
李莫愁
欧阳晓晓
黄蓉
黄药师 SkipWhile跳过名字小于4个字,并且索引小于2
欧阳晓晓
黄蓉
黄药师

5,Reverse 方法

Reverse 方法用于反转序列中的元素。

            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };

            foreach (var name in names.Reverse())
{
Console.WriteLine(name);
}

输出结果:

黄药师
黄蓉
欧阳晓晓
李莫愁
郭靖

6,Distinct 方法   

Distinct 方法用于去除重复元素。

            string[] names = { "郭靖", "郭靖", "李莫愁", "欧阳晓晓", "欧阳晓晓", "黄蓉", "黄药师" };

            Console.WriteLine("含有重复元素的数组");
foreach (var name in names)
{
Console.Write(name + " ");
}
Console.WriteLine("\n\n去除重复元素的数组");
foreach (var name in names.Distinct())
{
Console.Write(name + " ");
}
 

输出结果:

含有重复元素的数组
郭靖 郭靖 李莫愁 欧阳晓晓 欧阳晓晓 黄蓉 黄药师 去除重复元素的数组
郭靖 李莫愁 欧阳晓晓 黄蓉 黄药师

自定义IEqualityComparer<T>接口的相等比较器

 
        public class MyEqualityComparer<T> : IEqualityComparer<T>
{
#region IEqualityComparer<T> 成员 public bool Equals(T x, T y)
{
string temp = x as string;
if (temp != null)
{
if (temp == "欧阳晓晓") //对"欧阳晓晓"不过滤
return false;
}
if (x.GetHashCode() == y.GetHashCode())
return true;
else
return false;
} public int GetHashCode(T obj)
{
return obj.GetHashCode();
} #endregion
}
private void DistinctDemo()
{
string[] names = { "郭靖", "郭靖", "李莫愁", "欧阳晓晓", "欧阳晓晓", "黄蓉", "黄药师" }; Console.WriteLine("含有重复元素的数组");
foreach (var name in names)
{
Console.Write(name + " ");
}
Console.WriteLine("\n\n去除重复元素的数组,实现自定义IEqualityComparer<T>");
foreach (var name in names.Distinct(new MyEqualityComparer<string>()))
{
Console.Write(name + " ");
}
}
 

输出结果:

含有重复元素的数组
郭靖 郭靖 李莫愁 欧阳晓晓 欧阳晓晓 黄蓉 黄药师 去除重复元素的数组,实现自定义IEqualityComparer<T>
郭靖 李莫愁 欧阳晓晓 欧阳晓晓 黄蓉 黄药师

7,Union 方法

    Union 方法 用于合并两个序列,并去掉重复元素。

 
            string[] names = { "郭靖", "郭靖", "李莫愁", "欧阳晓晓", "欧阳晓晓", "黄蓉", "黄药师" };

            Console.WriteLine("含有重复元素的数组");

            foreach (var name in names)
{
Console.Write(name + " ");
}
Console.WriteLine("\n\n去除重复元素的数组,实现自定义IEqualityComparer<T>");
foreach (var name in names.Distinct(new MyEqualityComparer<string>()))
{
Console.Write(name + " ");
}
 

输出结果:

合并后的元素
郭靖 李莫愁 欧阳晓晓 黄蓉 黄药师 杨过

自定义IEqualityComparer<T>接口的相等比较器

            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
string[] names2 = { "郭靖", "杨过", "欧阳晓晓" }; Console.WriteLine("合并后的元素");
foreach (var name in names.Union(names2,new MyEqualityComparer<string>()))
{
Console.Write(name + " ");
}
 

输出结果:

合并后的元素
郭靖 李莫愁 欧阳晓晓 黄蓉 黄药师 杨过 欧阳晓晓

8,Concat 方法

Concat 方法 用于连接两个序列,与Union不同,它不会过滤重复的元素。

 
            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
string[] names2 = { "郭靖", "杨过", "欧阳晓晓" }; Console.WriteLine("合并后的元素");
foreach (var name in names.Concat(names2))
{
Console.Write(name + " ");
}
 

输出元素:

合并后的元素
郭靖 李莫愁 欧阳晓晓 黄蓉 黄药师 郭靖 杨过 欧阳晓晓

9,Intersect 方法

Intersect 方法用于生成两个序列的交集。

 
            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
string[] names2 = { "郭靖", "杨过", "欧阳晓晓" }; Console.WriteLine("相交的元素");
foreach (var name in names.Intersect(names2))
{
Console.Write(name + " ");
}
 

输出结果:

相交的元素
郭靖 欧阳晓晓

自定义IEqualityComparer<T>

 
        public class MyEqualityComparer<T> : IEqualityComparer<T>
{
#region IEqualityComparer<T> 成员 public bool Equals(T x, T y)
{
string temp = x as string;
if (temp != null)
{
if (temp == "欧阳晓晓") //对"欧阳晓晓"不过滤
return false;
}
if (x.GetHashCode() == y.GetHashCode())
return true;
else
return false;
} public int GetHashCode(T obj)
{
return obj.GetHashCode();
} #endregion
}
 
           string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
string[] names2 = { "郭靖", "杨过", "欧阳晓晓" }; Console.WriteLine("相交的元素");
foreach (var name in names.Intersect(names2,new MyEqualityComparer<string>()))
{
Console.Write(name + " ");
}

输出结果:

相交的元素
郭靖

10,Except 方法

Except 方法用于生成两个序列的差集。

注意:返回是第一个数组里,去掉指定数组里的元素后,剩下的一个序列。

它和Intersect方法不是互补的,不要搞混了。下面的“杨过”就不会输出。因为它是指定数组里的元素,和源数组一毛钱关系都没有。

 
            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
string[] names2 = { "郭靖", "杨过", "欧阳晓晓" }; Console.WriteLine("2个数组的不同元素");
foreach (var name in names.Except(names2))
{
Console.Write(name + " ");
}

输出结果:

2个数组的不同元素
李莫愁 黄蓉 黄药师

运用自定义IEqualityComparer<T>指定比较器。

 
            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
string[] names2 = { "郭靖", "杨过", "欧阳晓晓" }; Console.WriteLine("2个数组的不同元素");
foreach (var name in names2.Except(names,new MyEqualityComparer<string>()))
{
Console.Write(name + " ");
}

输出结果:

2个数组的不同元素
杨过 欧阳晓晓

11,Range 方法

Range 方法用于生成指定范围的整数序列。在BS程序中,经常需要分页显示,在页面中需要显示页面号码的链接,用这个方法可以生成页码的数组。

由于没有this关键字,它是一个普通的静态方法。

            int istart = 1;//起始页码
int iend = 12; //结束页码 var pages = Enumerable.Range(1, iend - istart + 1);
Console.WriteLine("输出页码");
foreach (var n in pages)
{
Console.Write("[{0}] ", n);
}

输出结果:

输出页码
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]

12,Repeat 方法

Repeat 方法用于生成指定数量重复元素的序列。由于没有this关键字,它是一个普通的静态方法。

            var people = new { Name = "郭靖", Age = 35 };//定义一个匿名类型

            var peoples = Enumerable.Repeat(people, 4);

            Console.WriteLine("包含4个匿名元素:");
foreach (var n in peoples)
{
Console.WriteLine("{0} {1} ", n.Name, n.Age);
}

输出结果:

包含4个匿名元素:
郭靖 35
郭靖 35
郭靖 35
郭靖 35

13,Empty 方法

Empty 方法用于获取一个指定类型参数的空序列。由于没有this关键字,它是一个普通的静态方法。

           var s = Enumerable.Empty<string>();

            Console.WriteLine("序列的元素数:{0} ", s.Count());

输出结果:

序列的元素数:0 

14,DefaultIfEmpty 方法

DefaultIfEmpty 方法用于获取序列,如果序列为空则添加一个类型的默认值。例如:如果元素为引用类型,添加null元素;元素为int类型,则添加int的默认值0。

 
            string[] names = { "郭靖", "李莫愁", "欧阳晓晓", "黄蓉", "黄药师" };
var intempty = Enumerable.Empty<int>();//空的Int类型序列
//没有找到元素的序列
var empty = from n in names
where n.Length == 5
select n;
Console.WriteLine("DefaultIfEmpty 返回有内容的序列");
foreach (var n in names)
{
Console.Write("{0} ", n);
}
Console.WriteLine("\nempty空序列元素数:{0}", empty.Count());
Console.WriteLine("empty空序列应用DefaultIfEmpty 后的元素数:{0}", empty.DefaultIfEmpty().Count());
Console.Write("empty空序列应用DefaultIfEmpty 后的元素值:");
foreach (var n in empty.DefaultIfEmpty())
{
if (n == null)
Console.Write("null");
}
Console.WriteLine("\n****************************************");
Console.WriteLine("intempty空序列元素数:{0}", intempty.Count());
Console.WriteLine("intempty空序列应用DefaultIfEmpty 后的元素数:{0}", intempty.DefaultIfEmpty().Count());
Console.Write("intempty空序列应用DefaultIfEmpty 后的元素值:");
foreach (var n in intempty.DefaultIfEmpty())
{
Console.Write(n);
}

输出结果:

DefaultIfEmpty 返回有内容的序列
郭靖 李莫愁 欧阳晓晓 黄蓉 黄药师
empty空序列元素数:0
empty空序列应用DefaultIfEmpty 后的元素数:1
empty空序列应用DefaultIfEmpty 后的元素值:null
****************************************
intempty空序列元素数:0
intempty空序列应用DefaultIfEmpty 后的元素数:1
intempty空序列应用DefaultIfEmpty 后的元素值:

这个方法还可以指定一个自定义的默认值。

            var intempty = Enumerable.Empty<int>();//空的Int类型序列
Console.Write("int 类型自定义默认值:");
foreach (var i in intempty.DefaultIfEmpty(200))
{
Console.Write(i);
}

输出结果:

int 类型自定义默认值:

7、Cast 方法

Cast 方法用于按照TResult类型转换IEnumerable序列的集合。

            //ArrayList没有实现IEnumerable<T>接口
ArrayList names = new ArrayList();
names.Add("郭靖");
names.Add("李莫愁");
names.Add("欧阳晓晓"); IEnumerable<string> newNames = names.Cast<string>();
foreach (var s in newNames)
{
Console.WriteLine(s);
}

输出结果:

郭靖
李莫愁
欧阳晓晓

8,OfType 方法

OfType 方法用于根据TResult类型筛选IEnumerable类型序列的元素。它的用途和Cast方法类似,但OfType方法如果遇到不能强制转换成TResutl的类型,会丢弃该元素,而不会出现运行错误。

            //ArrayList没有实现IEnumerable<T>接口
ArrayList names = new ArrayList();
names.Add("郭靖");
names.Add("李莫愁");
names.Add(100);
names.Add(new Stack());
names.Add("欧阳晓晓"); IEnumerable<string> newNames = names.OfType<string>();
foreach (var s in newNames)
{
Console.WriteLine(s);
}

输出结果:

郭靖
李莫愁
欧阳晓晓

9,AsEnumerable方法

AsEnumerable方法根据元素的类型转换为泛型IEnumerable<T>类型。

MSDN上的一个例子,AsEnumerable用于隐藏自己定义的和IEnumerable里的扩展方法同名的方法。

 
        public class MyList<T> : List<T>
{
public IEnumerable<T> Where(Func<T, bool> predicate)
{
Console.WriteLine("In MyList of Where");
return Enumerable.Where(this, predicate);
}
}
private void AsEnumerableDemo()
{
MyList<string> list = new MyList<string>() { "郭靖", "黄蓉", "黄药师" }; var query1 = list.Where(n => n.Contains("郭"));
Console.WriteLine("query1 created"); var query2 = list.AsEnumerable().Where(n => n.Contains("郭"));
Console.WriteLine("query2 created");
}

运行结果:

In MyList of Where
query1 created
query2 created

AsEnumerable方法经常用于Linq To SQL查询,将IQueryable<T>转换成IEnumerable<T>接口。因为一些扩展方法,如Reverse等,虽然在IQueryable<T>里有定义,但并不能将其翻译成对应的SQL语句,所以运行时会报错。用AsEnumerable方法转换成IEnumerable<T>后,实际的数据就在内存中操作。关于IQueryable<T>调用AsEnumerable背后的转换本质,有待进一步考证。

四、 IEnumberable & IQueryable 区别

在应用到IEnumberable 和IQueryable两个接口时,代码往往很相似,从而造成了很多困惑,然后事实上他们两是有很大的区别的,各种都有自己特定的使用场景。下面是IEnumberable和IQueryable的属性对比:

  IEnumerable  IQueryable
Namespace System.Collections Namespace System.Linq Namespace
继承于 No base interface 继承于 IEnumerable
Deferred Execution 支持 支持
Lazy Loading 不支持 支持
如何工作 当从数据库中查询数据,IEnumberable在服务器端执行查询操作,下载数据到客户端的内存中,然后再筛选数据,因此这个操作需要更多的工作而变得缓慢。 当从数据库中查询数据,IQueryable在服务器端根据所有的filter条件执行查询操作,因此该操作需要更少的工作而运行快。
适用于 LINQ to Object and LINQ to XML queries. LINQ to SQL queries.
自定义查询 不支持 支持使用CreateQuery 和Execute 方法。
Extension mehtod
parameter
Extension methods supported in IEnumerable takes functional
objects.
Extension methods supported in IEnumerable takes expression
objects i.e. expression tree.
使用场合 当从内存中的数据集合(如LIst,Array etc)查询数据的时候 当查询非内存中的数据集合(如远程数据库,service等)时。
最常使用 内存遍历 Paging

参考资料:http://www.cnblogs.com/xiashengwang/archive/2012/07/29/2613940.html

进阶系列(9)——linq的更多相关文章

  1. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  2. C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  3. C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建

    前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...

  4. DotNet进阶系列

    一. 回顾历史 回顾个人发展历程,自2012年初次接触开发至今(2018年)已经有六个年头,这期间陆陆续续学习并掌握了不少技术,C#语言.ORM框架.多线程技术.设计模式.前端技术.MVC.MVVM框 ...

  5. Ext JS学习第十六天 事件机制event(一) DotNet进阶系列(持续更新) 第一节:.Net版基于WebSocket的聊天室样例 第十五节:深入理解async和await的作用及各种适用场景和用法 第十五节:深入理解async和await的作用及各种适用场景和用法 前端自动化准备和详细配置(NVM、NPM/CNPM、NodeJs、NRM、WebPack、Gulp/Grunt、G

    code&monkey   Ext JS学习第十六天 事件机制event(一) 此文用来记录学习笔记: 休息了好几天,从今天开始继续保持更新,鞭策自己学习 今天我们来说一说什么是事件,对于事件 ...

  6. C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

    前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解  ...

  7. C#进阶系列——WebApi 接口参数不再困惑:传参详解

    前言:还记得刚使用WebApi那会儿,被它的传参机制折腾了好久,查阅了半天资料.如今,使用WebApi也有段时间了,今天就记录下API接口传参的一些方式方法,算是一个笔记,也希望能帮初学者少走弯路.本 ...

  8. C#进阶系列——WebApi 接口测试工具:WebApiTestClient

    前言:这两天在整WebApi的服务,由于调用方是Android客户端,Android开发人员也不懂C#语法,API里面的接口也不能直接给他们看,没办法,只有整个详细一点的文档呗.由于接口个数有点多,每 ...

  9. C#进阶系列——WebApi 跨域问题解决方案:CORS

    前言:上篇总结了下WebApi的接口测试工具的使用,这篇接着来看看WebAPI的另一个常见问题:跨域问题.本篇主要从实例的角度分享下CORS解决跨域问题一些细节. WebApi系列文章 C#进阶系列— ...

  10. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...

随机推荐

  1. PHP-----PHP程序设计基础教程----第二章PHP基本语法

    博文结构: 2.1 PHP语法风格 2.1.1 PHP标记 PHP有四种风格的标记,具体如表2-1所示: 表2-1 PHP开始和结束标记 标记类型 开始标记 结束标记 示例 说明 标准标记 <? ...

  2. 大专生自学html5到找到工作的心得

    先做个自我介绍,我13年考上一所很烂专科民办的学校,学的是生物专业,具体的学校名称我就不说出来献丑了.13年我就辍学了,我在那样的学校,一年学费要1万多,但是根本没有人学习,我实在看不到希望,我就退学 ...

  3. lnmp+coreseek实现站内全文检索(安装篇)

    coreseek安装与简单实用 安装环境 系统环境 centos7.2 1核2G 软件环境 coreseek-3.2.14 lnmp1.5 安装mmseg 更新依赖包和安装编译环境 yum -y in ...

  4. VS2015创建WDK的问题

    在微软官网找了半天.. 搜索window driver kit,好吧.进入一页英文页面.. https://docs.microsoft.com/en-us/windows-hardware/driv ...

  5. Python3爬虫(十八) Scrapy框架(二)

    对Scrapy框架(一)的补充 Infi-chu: http://www.cnblogs.com/Infi-chu/ Scrapy优点:    提供了内置的 HTTP 缓存 ,以加速本地开发 .   ...

  6. SDR软件无线电知识要点(三)EVM

    SDR软件无线电知识要点(三)EVM 信号质量如何评估 Noise Figure (NF) or sensitivity and Error Vector Magnitude (EVM) provid ...

  7. 20155339《java程序设计》第十二周课堂实践总结

    Arrays和String单元测试 在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常,错误和边界情况 String类 charAt split Arrays类 s ...

  8. 20145207 实验二《Java面向对象程序设计》实验报告

    20145207 实验二<Java面向对象程序设计>实验报告 实验内容 1.初步掌握单元测试和TDD 2.理解并掌握面向对象三要素:封装.继承.多态 3.初步掌握UML建模 4.熟悉S.O ...

  9. day4 基础

    1.变量 2.打印名片 3.raw_input() 和 input() 4.变量类型 type 5. if-else 6.标示符的规则 7.关键字 8.运算符 9.print打印多个值 1.变量 sc ...

  10. codevs 5429 多重背包

    5429 多重背包 http://codevs.cn/problem/5429 分析: f[i]=g[j-k*siz[i]]+k*val[i]; 发现一个状态d只会更新,d+siz[i],d+2*si ...