走进Linq-Linq大观园

本系列文章
走进Linq-Linq横空出世篇
走进Linq-辉煌的背后
走进Linq-Linq大观园
走进Linq-Linq to Objects(上)基础篇


文章发布后大家有些人叫做,心里窃喜,不过压力也大增,我很想按照简洁明快的文风写下去,不过讲技术的文章很难不落于沉闷,所以我努力了。(题外话:这几天猛看幽默小说,想把文字写的幽默一点,开个玩笑,呵呵)

经过几天的闭关编程大师又有了一些新的觉悟了,不管对DSL还是命令式编程和函数式编程都有了新的理解。如是他又接着了漫长的云游。第一站当然就是那个曾经让他结下心结的那个刚毕业的coder.
大师:“嘿,这几日可好,还在发牢骚么?”
Coder:“不了,正好你来了,让你看看我的程序”,Coder将他的电脑屏幕转向大师,期盼的眼神表明他急切的期望得到大师的夸奖。
如是大师看到了如下一些代码:

///   <summary>
    
///  一个通用的泛型委托,代表接受一个参数并有一个返回值的方法
    
///   </summary>
    
///   <typeparam name="TInput"> 输入参数类型 </typeparam>
    
///   <typeparam name="TOutput"> 返回值类型 </typeparam>
    
///   <param name="input"> 输入参数 </param>
    
///   <returns> 返回值 </returns>
     public   delegate  TOutput MyDelegate < TInput,TOutput > (TInput input);
    
///   <summary>
    
///  这个类是包含有对IEnumerable <T> 接口的一系列扩展方法
    
///  因为在.net里所有的集合类都实现了IEnumerable <T> 接口
    
///  所以对该接口的扩展将扩散到所有集合
    
///   </summary>
     public   static   class  Extension
    {
        
public   static  IEnumerable < TInput >  Where < TInput > ( this  IEnumerable < TInput >  self, MyDelegate < TInput,  bool >  filter)
        {
            
foreach  (TInput item  in  self)
                
if  (filter(item))
                    
yield   return  item;
        }
        
public   static  IEnumerable < TOutput >  Select < TInput, TOutput > ( this  IEnumerable < TInput >  self, MyDelegate < TInput, TOutput >  selector)
        { 
            
foreach (TInput item  in  self)
                
yield   return  selector(item);
        }
       
// 下面有更多的SQL风格的移植
}

下面是我做的个小测试代码:
public   class  Program
{
        
public  static  void  Main()
        {
            IList<Book> books = 
new  List<Book> { 
             
new  Book { Title =  " Inside COM " , ISBN =  " 123-456-789 " ,Price= 20  },
             
new  Book { Title =  " Inside C# " , ISBN =  " 123-356-d89 " ,Price= 100  },
             
new  Book { Title =  " Linq " , ISBN =  " 123-d56-d89 " , Price =  120  }
            };

            var result = books.Where(book => book.Title == 
" Linq " ).Select(book =>  new  { 
                Key = book.Title,Value=book.Price
            });
        }
}
Coder 一边展示着代码,一边念叨着,这里是因为使用了“扩展方法”所以可以这样写,这里使用了Lambda 表达式,它可以简化匿名方法的写法,这里……
编程大师一边听着coder 的讲解,一遍频频点头:“傻小子,不错啊,有点当年我的影子,按照你这样下去罗马也可以建成了,Linq 也是可以写出来的呀。”
Coder 听到大师的话兴奋异常,不过他从这句话里还是捕捉到了一个陌生的词汇:Linq 。他用诧异的眼神看着大师,问道:啥是Linq ,是谁家又创造了个新词汇?
大师笑着说,其实你刚才做的微软已经帮你做了,还给它起了一个非常洋气的名字:Linq ,中文名字呢就叫做 语言集成查询。
.net 3.5 发布的时候,微软新发布了几个dll ,其中有一个就叫做System.Core.dll ,在这个dll 下对一些System 命名空间做了进一步扩展。
在System.Core.dll下的System 命名空间下你会发现有这么几个泛型的委托:
// 无参,有一个返回值
public   delegate  TResult Func < TResult > ();
// 有一个参数和一个返回值,和你那个MyDelegate一样
public   delegate  TResult Func < T, TResult > (T arg);
// 两个参数一个返回值
public   delegate  TResult Func < T1, T2, TResult > (T1 arg1, T2 arg2);
// 三个参数一个返回值
public   delegate  TResult Func < T1, T2, T3, TResult > (T1 arg1, T2 arg2, T3 arg3);
// 四个参数一个返回值
public   delegate  TResult Func < T1, T2, T3, T4, TResult > (T1 arg1, T2 arg2, T3 arg3, T4 arg4);

有了这几个泛型的委托基本上都能应付了吧。
还是在这个dll 下有个System.Linq 命名空间,这是Linq 的核心命名空间,这里面有个名为 Enumerable 的静态类,它里面的方法都是对IEnumerable<T>( 这个接口可是Linq 的中心人物啊) 这个接口的扩展方法。看看,是不是在SQL 里能用的这里都能找到了?
Select,Where,OrderBy, OrderByDescending,Average,Distinct
所以你现在很简单的就可以写出像下面这样的代码了:
result = books.Where(book=>book.Title.StartsWith( " I " ))
     .OrderBy(book=>book.Price)
     .Select(book=>
new {Key=book.Title,Value=book.Price});
编程大师接着说:如果就这样算了,我觉得Linq 也不过尔尔,增加一些扩展方法而已,但是现在微软比我们想象的走的更远,现在你不仅仅能对程序中的一些集合对象做这样的查询了,你想想我们平时的程序可以归结为怎样一个等式?
还没等coder 说出口大师就在键盘上敲下:
程序=代码+数据
编程大师如是接着说:那这些数据平时都来源于哪里?
Coder :程序中自己构造的一些集合对象,像我刚才的代码中那样,还有数据库,这个使我们平时用到最多的,还有XML 存储,还有WebService ,这个来源于远程的数据,还有什么RSS 啦等等,很多了。
编程大师:嗯,是的。数据的来源非常广泛,就说我们平常用的三个吧,内存中的集合对象、XML 存储和数据库。对于内存中的集合对象我们有语言自身的支持,XML 我们有XML 的一些API ,比如XPath ,对于数据库我们有ADO.net ,可实际上从抽象层面我们对这些数据的操作都是相同的,你想不想屏蔽掉存储的细节,在高层有一个统一的API 访问这些数据呢?至于数据存储在哪里对于你是透明的,也许它存在于你内存中,也许在万网的机房也许在美国西雅图,但是对于你来说这些都无需关心,你的代码都一样。
Coder :听起来是个很美妙的事情,这不会是在做梦吧。
大师:不是在做梦,今天你已经有了这些方法
.net 3.5 里微软还发布了另外两个dll
System.Data.Linq.dll
System.Xml.Linq.dll
System.Data.Linq.dll 里,对数据库的查询做了支持,不过目前微软提供的只支持Sql Server ,感谢开源社区,现在有了DbLinq ,它提供了对MySql,Oracle,Sql Server,PostgreSql,Sqlite 的支持。
System.Xml.Linq.dll 在更高层次对Xml 的访问做了支持
这样你从微软这里获得了:
Linq to Objects 对内存中的集合的支持
Linq to Xml Xml 的支持
Linq to SQL Sql Server 的支持

 

这是一张从Linq in Action那本书里的截图,该图很好的在一个大的层次上揭示了Linq的视图。C#vb.net等一系列.net语言在一些语言特性和Linq对语言的扩展上对Linq家族提供了支持。未来我们将会实现Linq in EverywhereLinq将成为你的变成习惯。
C#Linq的语言层面支持
使用

result  =  books.Where(book => book.Title.StartsWith( " I " ))
                .OrderBy(book
=> book.Price)
                .Select(book
=> new {Key = book.Title,Value = book.Price});
这种方式编写代码,虽然减少了不少工作量,并且编程风格也更加人性化,不过现在 C# 为我们提供了一些列非常有用的关键字。上面的代码现在你可以这样来编写:
result  =  from book  in  books
        
where  book.Title.StartsWith( " I " )
        orderby book.Price
        select 
new  { Key = book.Title,Value  =  book.Price};
from 关键字后面跟着的是 Func 委托的输入参数,in 关键字后面跟着一个IEnumerable 类型,where,orderby,select 对应着那些扩展方法,那些扩展方法接受的委托的输入参数就是from 后面的book 。实际上你可以把这些代码写成一行意思就更明显了:
result  =  from book  in  books  where  book.Title.StartsWith( " I " ) orderby book.Price select  new  { Key = book.Title,Value  =  book.Price};
books 集合里枚举一个book ,如果这个book 的标题是以”I” 开头的就把它加入到返回集合中,并把返回集合按照book 的价钱排序 将上面的代码编译后的程序集用Reflector 反编译,生成的代码是:
books.Where < Book > ( delegate (Book book)
{
      
return  book.Title.StartsWith( " I " );
}).OrderBy
< Book,  float > ( delegate (Book book)
{
      
return  book.Price;
}).Select(
delegate (Book book)
{
      
return   new  { Key  =  book.Title, Value  =  book.Price };
});
看来这种方式写和扩展方法调用的方式没有什么差别,那为什么不呢。
废话那么多了,还是来几个HelloWorld 式的程序吧

HelloWorld Linq
(下面所有程序的Book就是本系列文章中第一篇所出现的Book)
有一个Book集合,但是这个集合具体存储哪里我们并不清楚,也许在内存,也许在数据库,也许在XML存储,我们要做的就是把价格大于50的给揪出来,然后按照价格排序。
Linq to Objects(从内存中的集合里查找)

数据准备阶段

// 这样的一个集合,存储在内存中
IList < Book >  books  =   new  List < Book >  { 
    
new  Book { Title  =   " Inside COM " , ISBN  =   " 123-456-789 " ,Price = 20  },
    
new  Book { Title  =   " Inside C# " , ISBN  =   " 123-356-d89 " ,Price = 100  },
    
new  Book { Title  =   " Linq " , ISBN  =   " 123-d56-d89 " , Price  =   120  }
};
数据查询阶段

var result = from book in  books
            
where book.Price > 50

            orderby book.Price
            select 
new { Key = book.Title, Value =  book.Price };
foreach (var item in
 result)
                Console.WriteLine(
"Key:{0}-Value:{1}",item.Key,item.Value.ToString());
Linq to SQL( 集合存储在Sql Server)

数据准备阶段
建立数据库表

输入数据

改写一下Book类,这个类是一个映射类,和数据库表做映射,更多内容后面会详细讲解

[Table]
public   class  Book
{
    
///   <summary>
    
///  图书名称
     
///   </summary>
    [Column]
    
public   string  Title {  get set ; }
    
///   <summary>
    
///  单价
     
///   </summary>
    [Column(DbType  =   " numeric(5, 2) " )]
    
public   float  Price {  get set ; }
    
///   <summary>
    
///  作者
     
///   </summary>
    [Column]
    
public   string  Author {  get set ; }
    
///   <summary>
    
///  ISBN号
     
///   </summary>
    [Column]
    
public   string  ISBN {  get set ; }
}
数据查询阶段
DataContext db  =   new  DataContext( " Data Source=localhost;Initial Catalog=db;User ID=sa;Password=sa " );
            var result 
=  from book  in  db.GetTable < Book > ()
                         
where  book.Price  >   50
                         orderby book.Price
                         select 
new  { Key  =  book.Title, Value  =  book.Price };

            
foreach  (var item  in  result)
                Console.WriteLine(
" Key:{0}-Value:{1} " ,item.Key,item.Value.ToString());
最后程序运行的结果都是:

真所谓殊途同归啊,不管数据是如何存储的,查询的方式却99%一致。

总结
本篇旨在给大家一个对Linq的大局观认识,没有详细的深入,就算一个总览吧。精彩无需等待,祝大家编程愉快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值