文章發布後大家有些人叫做,心裡竊喜,不過壓力也大增,我很想按照簡潔 明快的文風寫下去,不過講技術的文章很難不落於沉悶,所以我努力了。(題外 話:這幾天猛看幽默小說,想把文字寫的幽默一點,開個玩笑,呵呵)
經 過幾天的閉關編程大師又有了一些新的覺悟了,不管對DSL還是命令式編程和函 數式編程都有了新的理解。如是他又接著了漫長的雲游。第一站當然就是那個曾 經讓他結下心結的那個剛畢業的coder。
大師:“嘿,這幾日可好 ,還在發牢騷麼?”
Coder:“不了,正好你來了,讓你看看 我的程序”,Coder將他的電腦屏幕轉向大師,期盼的眼神表明他急切的期 望得到大師的誇獎。
如是大師看到了如下一些代碼:
///
/// 一個通用的泛型委托,代表接受一個參數並有一個返回值的方法
///
/// 輸入參數類型
/// 返回值類型
/// 輸入參數
/// 返回值
public delegate TOutput MyDelegate<TInput,TOutput>(TInput input);
///
/// 這個類是包含有對IEnumerable接口的一系列擴展方法
/// 因為在.net裡所有的集合類都實現了IEnumerable接口
/// 所以對該接口的擴展將擴散到所有集合
///
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 books = new List {
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(這個接口可是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
{
///
/// 圖書名稱
///
[Column]
public string Title { get; set; }
///
/// 單價
///
[Column(DbType = "numeric(5, 2)")]
public float Price { get; set; }
///
/// 作者
///
[Column]
public string Author { get; set; }
///
/// ISBN號
///
[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的大局觀認識,沒有詳細的深入,就 算一個總覽吧。精彩無需等待,祝大家編程愉快。