LINQ入门(中篇)

来自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=163
GPS平台、网站建设、软件开发、系统运维,找森大网络科技!
http://cnsendnet.taobao.comphp

到如今为止你还未触碰LINQ,那进来吧 —— LINQ入门(中篇)
前 言
  在上篇中简单的分享了LINQ的基础概念及基础语法,若是没有阅读过上篇的朋友能够点击这里。感谢你们的支持,本篇咱们将更进一步的学习LINQ的一些相关特性及应用方法。废话很少说,请往下阅读吧。
延迟加载
  在上篇中简单的和你们提到了LINQ具备一个颇有意思的特性那就是“延迟加载”(或“延迟计算”),什么是延迟加载呢?先看来自官方的描述:延迟执行意味着表达式的计算延迟,直到真正须要它的实现值为止。是否是以为有点生涩难理解呢?按照我我的的理解通俗的讲就是,每当咱们编写好一段LINQ表达式时,此时这个表达式所表明的序列变量仅仅只是一个代理,编译器在执行编译时根本就不鸟这段代码,检查完语法正确性后直接跳过,直到代码在编译器动态运行序列变量在其余代码块被调用时,它所代理的linq表达式才会执行。啊~~看到这里你是否是要晕了,到底要怎么理解啊,无废话上代码:sql

1 // 已知一个序列
 2 var array = new int[] {1, 2, 3};
 3 
 4 // 编写一段LINQ表达式得到一个序列变量query
 5 // 注意,这个变量仅仅是一个代理,在执行编译的时候,编译器检查完
 6 // 该代码的正确性后直接跳过,再也不理会
 7 var query = from arr in array
 8             where arr > 1
 9             select arr;
10 
11 // 调用上述序列变量query,此时上述的LINQ表达才会执行。注意此时已经是在
12 // 编译器Runtime 的状况下执行
13 foreach(var q in query)
14     Console.WriteLine(q.ToString());

若是你以为上述例子不能让你有个深入的理解,那么请看来自MSDN的例子express

1 public static class LocalExtensions
 2 {
 3     public static IEnumerable<string>
 4       ConvertCollectionToUpperCase(this IEnumerable<string> source)
 5     {
 6         foreach (string str in source)
 7         {
 8             Console.WriteLine("ToUpper: source {0}", str);
 9             yield return str.ToUpper();
10         }
11     }
12 }
13 
14 class Program
15 {
16     static void Main(string[] args)
17     {
18         string[] stringArray = { "abc", "def", "ghi" };
19         // 这里方法 ConvertCollectionToUpperCase 是不会在编译时进行调用核查的,直到下面的foreach调用变量 q 此方法才会执行
20         var q = from str in stringArray.ConvertCollectionToUpperCase()
21                 select str;
22 
23         foreach (string str in q)
24             Console.WriteLine("Main: str {0}", str);
25     }
   }

注意,ConvertCollectionToUpperCase 是一个静态扩展方法,后续讲解,若是你对.net 2.0 的 yeild 不熟悉的网上查阅吧,这里就不作介绍了。网络

// 输出结果
// ToUpper: source abc
// Main: str ABC
// ToUpper: source def
// Main: str DEF
// ToUpper: source ghi
// Main: str GHI运维

  小结,延迟加载有好也有坏,因为是在Runtime的状况下执行序列,因此就容易形成未知异常,断点打错等等,因此编码LINQ是必定要考虑到它的这个特性。
lambda 表达式
  了解完延迟加载后,那么如今咱们须要简单的学习一下.net 3.5 给咱们带来的新特性lambda表达式,在上篇的评论中,有园友问lambda和linq有什么关系,在这里其实他们没有任何关系,是彻底不一样的东西,可是咱们为何要掌握它呢?由于在后续的学习中会使用大量的lambda表达,他可使咱们的代码更优雅更有可读性,大大提升了咱们的编码效率。
  那么在学习lambda以前,先来回顾一下.net 2.0给咱们带来的委托 delegate ,这个你必定不会感到陌生吧,并且必定会经常使用他。对于委托这里就不作详细的介绍了,要复习委托的在网上查阅吧。经过委托,咱们能够获得一个东西“匿名方法”。咦,是否是以为很眼熟,呵呵,用代码来加深回忆吧ide

public delegate void SomeDelegate1;
public delegate void SomeDelegate2(arg 1, arg 2);

// 匿名方法
SomeDelegate1 del1 += delegate() {...};
SomeDelegate2 del2 += delegate(arg1, arg2) {...}

上面的代码中咱们看到在.net 2.0时代,咱们能够经过delegate建立匿名方法,提升编码的灵活性,那么lambda和这个有什么关系呢,lambda对匿名方法进行了升华。看代码:函数

public delegate void SomeDelegate1;
public delegate void SomeDelegate2(arg 1, arg 2);

// 匿名方法
SomeDelegate1 del1 += () => {...};
SomeDelegate2 del2 += (arg1, arg2) => {...}

呵呵,是否是以为有点难以想象呢,言归正传什么是lambda表达式呢,来自官方的定义:“Lambda 表达式”是一个匿名函数,它能够包含表达式和语句,而且可用于建立委托或表达式树类型。全部 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to”。 该 Lambda 运算符的左边是输入参数(若是有),右边包含表达式或语句块。 Lambda 表达式 x => x x 读做“x goes to x times x”。在定义里提到了表达式树,这是高阶晋级的话题,这里就不作讨论了,咱们先把精力放在入门与实战应用上。
常规的lambda表达式以下:
(parameters) => {expression}
当指定的委托类型没有参数是表达式能够以下
() => {expression} 例:() => {/
执行某些方法*/}
若是表达右侧花括号里只有一个表达例如一元表达式,二元表达式等等,又或者是一个方法时那么花括号能够省略以下:
(x) => x; // 最简表达式学习

(x, y) => x == y;网站

() => SomeMethod();
注意,若是右侧的表达式存在花括号"{}",并且委托是具备返回类型的,那么表达式必须带上 return 关键字,以下:
(x, y) => {return x == y;};
到此咱们已对lambad 表达式有了必定的掌握与了解。那么咱们扩展一下,在.net 3.5中,ms 给咱们提供了两个泛型委托 分别是 Fun<T> 和 Action <T> 他们能够帮助咱们省去了返回建立经常使用委托的麻烦,提升编码效率。
共同点:它们至多提供委托传递6个参数(任意类型);
不一样点:Fun 要求必须具备返回类型,而Action则必须不返回类型,规定返回 void
示例:
Fun<int, int, bool> fun = (a, b) => a==b;this

Action<string> action = (p) => Console.Write(p);
小结,lambda 对我我的而言是个又爱又恨啊,不过爱多一点,它使咱们写更少的代码作更多的事,可是在调试时一旦修改表达式内容,那么当前调试要么中止,要么从新开始,ms要是在这方面作得更完美些就好啦。不过它也间接提醒咱们要有好的编码设计思惟。
静态扩展方法
  说完lambda,那么咱们就进一步了解一下.net 3.5的另外一个新特性“静态扩展方法”,什么是静态扩展方法呢,官方定义:扩展方法使您可以向现有类型“添加”方法,而无需建立新的派生类型、从新编译或以其余方式修改原始类型。 扩展方法是一种特殊的静态方法,但能够像扩展类型上的实例方法同样进行调用。简单的说就是咱们能够向一个已知的类型在不经过继承,复写等操做的状况下添加一个方法,以便类型的实例能够直接使用该方法。示例以下:

static class A
{
    // 这里的p1,p2 仅做为示例,实际中咱们不必定须要
    public static int ExtendMethod1(this string input,string p1, string p2)
    {
         return int.Parse(input + p1 + p2);
    }

     // 泛型方法
     public static TOutput, ExtendMethod2<TOutput>(this object obj);
     {
           return (TOutput)obj;
     }
}

class B
{
  void Main()
   {
      var a = "1";
      var result = a.ExtendMethod1("2","3");
      // result:123
   }
}

注意,方法的 static 是必须的,并且需在静态类里。第一个参数 this 是必须的,紧跟着 this 后面的须要扩展的类型实例参数,也是必须的。至于后面的方法调用传递参数就因我的所需了。
  既然咱们学习了静态扩展方法,那么它和LINQ又有什么关系呢?在System.Linq的命名空间中提供了大量的静态扩展方法,这些静态方法自己就对linq表达式的封装,这样咱们就能够省去了编写简单的linq表达式的步骤。以下:

var array = new int[]{1,2,3,4,5};

var query1 = from arr in array
             select arr;

var query2 = array.Select(e => e);

上面的示例中 query1 和 query2 是等价的,经过 query2 咱们是否是又能够偷懒了不少,呵呵。
再来点带where的

var array = new int[]{1,2,3,4,5};

var query1 = from arr in array
             where arr > 2
             select arr;

var query2 = array.Where(e => e > 2);

再来一个复合型的
var array = new int[]{1,2,3,4,5};

var max = (from arr in array.Where(e => e > 2)
           select arr).Max();

是否是以为很cool。因为篇幅的关系在这里就不逐一的去接受这些静态方法了,下面是一些经常使用的静态方法列表,感兴趣的去MSDN查阅详细吧。
Aggregate , All , Any , AsEnumerable , Average , Cast , Concat , Contains, Count, DefaultIfEmpty , Distinct , ElementAt, ElementAtOrDefault ,Empty , Except , First, FirstOrDefault , GroupBy , GroupJoin , Intersect , Join , Last , LastOrDefault , LongCount , Max , Min , OfType ,OrderBy ,OrderByDescending , Range , Repeat , Reverse , Select , SelectMany , SequenceEqual , Single , SingleOrDefault , Skip , SkipWhile , Sum ,Take, TakeWhile , ThenBy , ThenByDescending , ToArray , ToDictionary , ToList, ToLookup, Union,Where
Cast<T> 和 OfType<T> 静态扩展方法
  在最后咱们仍是要注意两个经常使用的静态方法Cast<T>, OfType<T> 。它们的共同点是都能把非IEnumerable<T> 类型的集合转换成IEnumerable<T>类型,而后再
进行LINQ操做,以下

var dt = new DataTable();
dt.Columsn.Add("A", typeof(int));

var newRow1 = dt.NewRow();
newRow1["A"] = 1;

var newRow2 = dt.NewRow();
newRow2["A"] = 2;

dt.Rows.Add(newRow1);
dt.Rows.Add(newRow2);

var query1 = dt.Rows.Cast<DataRow>().Select(e=>(int)e["A"]);
var query2 = dt.Rows.OfType<DataRow>().Select(e=>(int)e["A"]);

这样咱们就能够获得看上去两个相同的序列,在这里要注意:MSDN上的说明存在误导,MSDN对于OfType<T>的解释存在误差,实际上经本人反复敲代码验证,获得的结论是,Cast对序列进行强制转换,一旦转换不成功则抛出异常。OfType则是一旦转换不成功,则不会抛出异常,可是将会获得一个空序列。见下面代码:

var arr1 = new string[] { "1","2","test" };
var arr2 = arr1.Cast<int>();
var arr3 = arr1.OfType<int>();

//经过Cast转换,则会抛出异常
foreach (var i in arr2)
     Console.WriteLine(i.ToString());

//经过OfType转换,有异常可是不会抛出并获得一个空序列
foreach (var i in arr3)
      Console.WriteLine(i.ToString());

Console.Read();

总 结
  本文到此,咱们已对LINQ涉及的应用有了进一步的了解。学习什么是linq的延迟加载,lambda和linq是否有暧昧关系。以及静态扩展方法对linq的辅助做用。也许你会问既然能够用静态扩展方法替代编写linq,那么两者怎么择取呢,据砖家叫兽提议咱们应该先以linq命名空间下的静态扩展方法为主,实在是很复杂的linq表达式,咱们再考虑使用linq自己的表达式编写。后续咱们将分享学习LINQ更贴近实战应用的知识,linq to dataset, linq to xml, linq to sql, linq to entities.
  感谢您的阅读,若是有说得不对的地方请指正。

来自森大科技官方博客
http://www.cnsendblog.com/index.php/?p=163
GPS平台、网站建设、软件开发、系统运维,找森大网络科技!
http://cnsendnet.taobao.com