本系列文章
走进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 Everywhere,Linq将成为你的变成习惯。
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的大局观认识,没有详细的深入,就算一个总览吧。精彩无需等待,祝大家编程愉快。
posted @ 2008-07-18 11:26
横刀天笑 阅读(1612)
评论(21) 编辑 收藏 所属分类:
走进Linq