LINQ准备篇--学习linq的资料和笔记(一)(转)

 

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()。

[csharp] view plaincopyprint?
 
  1. static class MyExtensions  
  2.         {  
  3.             // 本方法允许任何对象显示它所处的程序集  
  4.             public static void DisplayDefiningAssemlby(this object obj)  
  5.             {  
  6.                 Console.WriteLine("{0} is defined in: \n\t {1}\n",  
  7.                     obj.GetType().Name,  
  8.                     System.Reflection.Assembly.GetAssembly(obj.GetType()));  
  9.             }  
  10.         }  

调用扩展方法

我们有两种方式来使用扩展方法,第一种是在实例层次上调用扩展方法,第二种是静态调用扩展方法。

[csharp] view plaincopyprint?
 
  1. public void UsingExtensionMethods()  
  2.         {  
  3.             int myInt = 12345;  
  4.    
  5.             // 1. 在实例层次上调用扩展方法  
  6.             myInt.DisplayDefiningAssemlby();  
  7.    
  8.             // 2. 静态调用扩展方法  
  9.             MyExtensions.DisplayDefiningAssemlby(myInt);  
  10.         }  

实例上,通过一个对象调用它的扩展方法只是编译器的烟幕弹效果而已,背后编译器会转换成静态方法的调用。

其他注意事项

上面说到,扩展方法本质上是可以从扩展类型的实例上调用的静态方法。所以它和普通的方法是不一样的,扩展方法不能直接访问扩展类型的成员,从另外一个角度讲,扩展方法即不是直接修改,也不是继承。

另外一个要注意的地方是:虽然表面上扩展方法是全局的,但其实他们受制于所处的命名空间,要使用在其他命名空间中定义的扩展方法时,我们首先需要导入该命名空间。


(3).对象初始化器

  1. User user = new User();  
  2. user.Id = 1;  
  3. user.Name = "YJingLee";  
  4. user.Age = 22; 

    在.net3.5中,编译器会自动地生成合适的属性setter代码,使得原来几行的属性赋值操作可以在一行完成。我们可以这样简化:像这样,对象初始化器由一系列成员对象组成,其对象必须初始化,用逗号间隔,使用{}封闭。

  1. User user = new User { Id = 1, Name = "YJingLee", Age = 22 }; 

除了在初始化类时设置简单的属性值外,C#对象初始化器特性也允许我们设置更复杂的嵌套(nested)属性类型。例如我们可以在上面定义的User类型同时拥有一个属于Address类型的叫“Address”的属性:

  1. User user = new User  
  2. {  
  3. Id = 1,  
  4. Name = "YJingLee",  
  5. Age = 22,  
  6. Address = new Address  
  7. {  
  8. City = "NanJing",  
  9. Zip = 21000 
  10. }  
  11. }; 

 

(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()
{
// 构造一个匿名对象表示一个雇员
var worker = new { FirstName = "Vincent", LastName = "Ke", Level = 2 };

// 显示并输出
Console.WriteLine("Name: {0}, Level: {1}", worker.FirstName + "" + worker.LastName, worker.Level);
}
使用上述代码来构建匿名对象时,C#编译器会在编译时自动生成名称唯一的类。因为这个类的名字在C#中是不可见的,所以必需使用var关键字来使用隐式类型化。另外,我们需要通过对象初始化语法来定义一系列属性来封装各个数据。

 

匿名类型也可以添加方法,使用泛型委托 来给匿名类来添加方法

如下:

[csharp] view plaincopyprint?
 
  1. Func<string, string, string> getAllName = delegate(string first,string last)   
  2. {   
  3.     return string.Format("{0} {1}", first, last);   
  4. };   
  5.   
  6. var myObj = new { firstName = "allen", secondName = "Liu", getName = getAllName };   
  7. //这里需要传递两个参数,有点郁闷   
  8. string myName = myObj.getName(myObj.firstName,myObj.secondName);   
  9. Console.WriteLine(myName);   



 

以上是我学习linq准备的四个方向的知识!

然后,进入正题

 linq简单介绍

LINQ中最基本的数据单元是sequences和elements。一个sequence是实现了IEnumerable<T>的对象,而一个element是sequence中的每一个元素。如下,names就是一个sequence,”Tom”,“Dick”和”Harry”则是elements。

[csharp] view plaincopyprint?
 
  1. string[] names = { "Tom", "Dick", "Harry" };  


 

一个查询运算符就是用来转换sequence的方法。一个典型的查询运算符接收一个输入sequence并输出一个转换之后的sequence。在System.Linq.Enumerable类中,总共定义了40来个查询运算符----全部用扩展方法来实现,他们被称为标准查询运算符。

一个查询则是由查询运算符来转换sequence的一个表达式,最简单的查询由一个input sequence和一个查询运算符组成。比如:

[csharp] view plaincopyprint?
 
  1. string[] names = { "Tom", "Dick", "Harry"   };  
  2.   
  3.    // 获取所有长度大于等于4的名字  
  4.    IEnumerable<string> filteredNames = System.Linq.Enumerable.Where(  
  5.        names, n => n.Length >= 4);  
  6.   
  7.    foreach (string n in filteredNames)  
  8.        Console.WriteLine(n);  


 

输出:
Dick
Harry

因为查询运算符是以扩展方法实现的,所以我们可以直接在names对象上调用Where:

[csharp] view plaincopyprint?
 
  1. // 获取所有长度大于等于4的名字  
  2.     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的必需知识!

posted on 2015-05-20 11:50  高达  阅读(178)  评论(0)    收藏  举报

导航