LINQ准备篇--学习linq的资料和笔记(一)(转)
学习linq 的前提知识:
(1).var 隐式推断类型,
VAR 是3.5新出的一个定义变量的类型
其实也就是弱化类型的定义
VAR可代替任何类型
编译器会根据上下文来判断你到底是想用什么类型的
使用var定义变量时有以下四个特点:
1. 必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式:
var s;
s = “abcd”;
2. 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。
3. var要求是局部变量。
4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。
(2).扩展方法
下一个与LINQ密切相关的C# 3.0语言功能是扩展方法(Extension method)。在这之前,一旦一个类型被编译进.NET程序集后,我们便不能再修改该类型的定义了。为该类型添加、修改、删除成员的唯一办法就是修改类型的定义代码。
但有时候,当需要为类型添加新功能但并不拥有类型的已有代码时,比如,我们想要为.NET库类型List添加自定义的dump方法时,该怎么做呢,答案是扩展方法。扩展方法允许在不修改类型定义的情况下,让该类型获得功能上的扩展。
定义扩展方法
当定义一个扩展方法时,第一个限制就是必须把方法定义在静态类中,因此每一个扩展方法也必须声明为静态的。第二个限制是扩展方法要用this关键字对第一个参数进行修饰,这个参数也就是我们希望进行扩展的类型。
比如下面的扩展方法允许.NET基类库中的所有对象都拥有全新的方法DisplayDefiningAssembly()。
- static class MyExtensions
- {
- // 本方法允许任何对象显示它所处的程序集
- public static void DisplayDefiningAssemlby(this object obj)
- {
- Console.WriteLine("{0} is defined in: \n\t {1}\n",
- obj.GetType().Name,
- System.Reflection.Assembly.GetAssembly(obj.GetType()));
- }
- }
调用扩展方法
我们有两种方式来使用扩展方法,第一种是在实例层次上调用扩展方法,第二种是静态调用扩展方法。
- public void UsingExtensionMethods()
- {
- int myInt = 12345;
- // 1. 在实例层次上调用扩展方法
- myInt.DisplayDefiningAssemlby();
- // 2. 静态调用扩展方法
- MyExtensions.DisplayDefiningAssemlby(myInt);
- }
实例上,通过一个对象调用它的扩展方法只是编译器的烟幕弹效果而已,背后编译器会转换成静态方法的调用。
其他注意事项
上面说到,扩展方法本质上是可以从扩展类型的实例上调用的静态方法。所以它和普通的方法是不一样的,扩展方法不能直接访问扩展类型的成员,从另外一个角度讲,扩展方法即不是直接修改,也不是继承。
另外一个要注意的地方是:虽然表面上扩展方法是全局的,但其实他们受制于所处的命名空间,要使用在其他命名空间中定义的扩展方法时,我们首先需要导入该命名空间。
(3).对象初始化器
- User user = new User();
- user.Id = 1;
- user.Name = "YJingLee";
- user.Age = 22;
在.net3.5中,编译器会自动地生成合适的属性setter代码,使得原来几行的属性赋值操作可以在一行完成。我们可以这样简化:像这样,对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。
- User user = new User { Id = 1, Name = "YJingLee", Age = 22 };
除了在初始化类时设置简单的属性值外,C#对象初始化器特性也允许我们设置更复杂的嵌套(nested)属性类型。例如我们可以在上面定义的User类型同时拥有一个属于Address类型的叫“Address”的属性:
- User user = new User
- {
- Id = 1,
- Name = "YJingLee",
- Age = 22,
- Address = new Address
- {
- City = "NanJing",
- Zip = 21000
- }
- };
(4).匿名方法
C#为委托提供一种机制,可以为委托定义匿名方法,匿名方法没有名称,编译器会定指定一个名称,如下
button1.Click += delegate(System.Object o, System.EventArgs e) { System.Windows.Forms.MessageBox.Show("Click!"); };
C#3.0之后匿名方法可以使用λ表达式来进行定义
λ运算符 =>
左边是参数,使用括号表达 (string param),可以是 (param)这样不定义类型,编译器会推断出来,只有一个参数的时候可以不使用括号
右边是实现代码,使用花括号,如果代码只有一行,则不使用花括号和return关键字也可以,编译器会为我们添加
这是λ表达式的简单实现
string str1 = "a";
string str2 = "b";
MyDelagate my = param => param + str1 + str2;
Console.WriteLine(my("c"));
输出"cab"
(5)匿名类型
static void TestAnonymousType()使用上述代码来构建匿名对象时,C#编译器会在编译时自动生成名称唯一的类。因为这个类的名字在C#中是不可见的,所以必需使用var关键字来使用隐式类型化。另外,我们需要通过对象初始化语法来定义一系列属性来封装各个数据。
{
// 构造一个匿名对象表示一个雇员
var worker = new { FirstName = "Vincent", LastName = "Ke", Level = 2 };
// 显示并输出
Console.WriteLine("Name: {0}, Level: {1}", worker.FirstName + "" + worker.LastName, worker.Level);
}
匿名类型也可以添加方法,使用泛型委托 来给匿名类来添加方法
如下:
- Func<string, string, string> getAllName = delegate(string first,string last)
- {
- return string.Format("{0} {1}", first, last);
- };
- var myObj = new { firstName = "allen", secondName = "Liu", getName = getAllName };
- //这里需要传递两个参数,有点郁闷
- string myName = myObj.getName(myObj.firstName,myObj.secondName);
- Console.WriteLine(myName);
以上是我学习linq准备的四个方向的知识!
然后,进入正题
linq简单介绍
LINQ中最基本的数据单元是sequences和elements。一个sequence是实现了IEnumerable<T>的对象,而一个element是sequence中的每一个元素。如下,names就是一个sequence,”Tom”,“Dick”和”Harry”则是elements。
- string[] names = { "Tom", "Dick", "Harry" };
一个查询运算符就是用来转换sequence的方法。一个典型的查询运算符接收一个输入sequence并输出一个转换之后的sequence。在System.Linq.Enumerable类中,总共定义了40来个查询运算符----全部用扩展方法来实现,他们被称为标准查询运算符。
一个查询则是由查询运算符来转换sequence的一个表达式,最简单的查询由一个input sequence和一个查询运算符组成。比如:
- string[] names = { "Tom", "Dick", "Harry" };
- // 获取所有长度大于等于4的名字
- IEnumerable<string> filteredNames = System.Linq.Enumerable.Where(
- names, n => n.Length >= 4);
- foreach (string n in filteredNames)
- Console.WriteLine(n);
输出:
Dick
Harry
因为查询运算符是以扩展方法实现的,所以我们可以直接在names对象上调用Where:
- // 获取所有长度大于等于4的名字
- IEnumerable<string> filteredNames = names.Where(n => n.Length >= 4);
我们可以通过使用var关键字来进一步简写我们的query:
var filteredNames = names.Where(n => n.Length >= 4);
注:在初学LINQ时,var关键字可能会影响可读性,特别是在没有IDE和智能提示的时候,因而如果可能,我会在本系列的开始尽量使用确切的返回类型。
大部分查询运算符都接受一个lambda表达式作为参数,lambda表达式决定了查询的行为特性和结果。在上例中,lambda表达式为:
n => n.Length >= 4
Lambda表达式格式为:(parameters) => expression-or-statement-block
在这里的lambda表达式中,输入参数n对应了names数组的每一个元素,其类型为string。Where运算符要求lambda表达式返回一个bool值,当结果为true时,表示该元素会包含在输出sequence中。这里是Where运算符的方法签名:
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
下面的query获取所有包含字母a的名字:
string[] names = { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredNames = names.Where(n => n.Contains("a"));
foreach (string name in filteredNames)
Console.WriteLine(name); // Harry
到现在为止,我们通过使用扩展方法和lambda表达式来建立了LINQ query。我们很快就能看到,这种策略非常的灵活和适合query的创建,因为我们可以级联的使用查询运算符。通常,这种方法被称为LINQ方法语法(英文著作中称为fluent systax)。C#还提供了另外一种书写query的语法,叫做查询表达式语法(英文著作中称为query expression systax),下面是一个用查询表达式语法建立的query,让我们先睹为快:
IEnumerable<string> filteredNames = from n in names
where n.Contains("a")
select n; // Harry
以上内容简单介绍了linq 的使用和学习linq的必需知识!
浙公网安备 33010602011771号