(原創(chuàng):灰灰蟲(chóng)的家http://hi.baidu.com/grayworm) LinQ家族五大成員: LinQ to Objects - 默認(rèn)功能,用來(lái)實(shí)現(xiàn)對(duì)內(nèi)存中集合對(duì)象的查詢 LinQ to SQL - 針對(duì)SQL Server的查詢,它是一個(gè)帶有可視化的操作界面的ORM工具 LinQ to DataSet - 對(duì)強(qiáng)類型化或弱類型化的DataSet或獨(dú)立的DataTable進(jìn)行查詢 LinQ to Entity - 對(duì)實(shí)體框架中EDM定義的實(shí)體集合進(jìn)行查詢。 LinQ to XML - 對(duì)XML文檔進(jìn)行查詢創(chuàng)建等操作。 C#語(yǔ)法與LinQ相關(guān)的新增功能
1.隱式強(qiáng)類型變量 在C#3.0中可以使用var關(guān)鍵字隱式定義強(qiáng)類型局部變量。
《圖1》 這里的var關(guān)鍵字定義變量與JavaScript定義變量看起來(lái)很像但二者有著本質(zhì)的區(qū)別。 JavaScript定義的變量是弱類型的變量,也可理解為是一種通用類型的變量,它可以容納各種類型的值,還可以在運(yùn)行過(guò)程中動(dòng)態(tài)修改其中的內(nèi)容類型。下面這種寫法在JavaScript中是正確的: var obj = 3.14; obj = "hello world"; C#中的var則是種強(qiáng)類型的變量,它在定義的時(shí)候會(huì)確定數(shù)據(jù)類型,分配內(nèi)存空間。上面這兩名代碼在C#3.0中會(huì)報(bào)錯(cuò),因?yàn)榈谝痪湟呀?jīng)把obj定義為double型的變量,第二句把字符串賦值給double是錯(cuò)誤的操作。
隱式強(qiáng)類型并不能有效簡(jiǎn)化我的書(shū)寫的代碼,但當(dāng)我們?cè)谟盟鼇?lái)動(dòng)態(tài)接收一些未知類型的數(shù)據(jù)的時(shí)候就顯雖得很強(qiáng)大。在隱式強(qiáng)類型變量出現(xiàn)前,我們一般是使用object型變量來(lái)接收這種未知類型的數(shù)據(jù)的。 2.對(duì)象初始化 這個(gè)功能可以有效簡(jiǎn)化類的getter和setter部份的代碼,還可以只使用一行表達(dá)式語(yǔ)句實(shí)現(xiàn)對(duì)象的實(shí)例化與初始化操作。 如定義實(shí)例類時(shí)可以使用如下代碼,沒(méi)有必要再把成員變量和屬性分別定義了。 public class LineItem { public int OrderID { get; set; } public int ProductID { get; set; } public short Quantity { get; set; } public string QuantityPerUnit { get; set; } public decimal UnitPrice { get; set; } public float Discount { get; set; } } 實(shí)例化LineItem對(duì)象,并為它賦值 var line3 = new LineItem { OrderID = 11000, ProductID = 61, Quantity = 30, QuantityPerUnit = “12 1-kg cartons”, UnitPrice = 15.55M, Discount = 0.15F }; 3.數(shù)組初始化 var LineItems = new[] { new LineItem {OrderID = 11000, ProductID = 11, Quantity = 10, QuantityPerUnit = “24 500-g bottles”, UnitPrice = 15.55M, Discount = 0.0F}, new LineItem {OrderID = 11000, ProductID = 21, Quantity = 20, QuantityPerUnit = “12 1-kg cartons”, UnitPrice = 20.2M, Discount = 0.1F}, new LineItem {OrderID = 11000, ProductID = 31, Quantity = 30, QuantityPerUnit = “24 1-kg bags”, UnitPrice = 25.45M, Discount = 0.15F} }; 4.集合初始化 var LineItemsList = new List < LineItem > { new LineItem {OrderID = 11000, ProductID = 11, Quantity = 10, QuantityPerUnit = “24 500-g bottles”, UnitPrice = 15.55M, Discount = 0.0F}, new LineItem {OrderID = 11000, ProductID = 21, Quantity = 20, QuantityPerUnit = “12 1-kg cartons”, UnitPrice = 20.2M, Discount = 0.1F}, new LineItem {OrderID = 11000, ProductID = 31, Quantity = 30, QuantityPerUnit = “24 1-kg bags”, UnitPrice = 25.45M, Discount = 0.15F} }; 5.匿名類型 過(guò)去我們要生成對(duì)象時(shí),必須事先定義該對(duì)象的類,然后使用new關(guān)鍵字來(lái)實(shí)例化該類。匿名類型簡(jiǎn)化定義類的這個(gè)過(guò)程,我們可以使用new關(guān)鍵字直接把類的定義,類的實(shí)例化放在一個(gè)表達(dá)式語(yǔ)句中。 如: var obj = new { Name = "zhangsan", Age = 18, URL = "}; Console.WriteLine(obj.Name + obj.Age + obj.URL); 匿名類型主要用在LinQ to SQL中對(duì)字段的投影功能上: var query = from i in LineItems select new { i.OrderID, i.ProductID, i.UnitPrice } 6.擴(kuò)展方法 擴(kuò)展方法就是為現(xiàn)有的類追加我們自定義的方法。在C#3.0的集成開(kāi)發(fā)環(huán)境中,我們會(huì)發(fā)現(xiàn)帶有向下箭頭的方法,這些方法是我們?cè)贑#2.0中所沒(méi)有見(jiàn)到的方法,這些方法就是我們所謂的“擴(kuò)展方法”,它是C#3.0在C#2.0的基礎(chǔ)上新增的一系列的方法,當(dāng)然我們也可以為內(nèi)置類添加我們自己的主擴(kuò)展方法。
《圖2》 例如在string中有個(gè)Length()擴(kuò)展方法,它用來(lái)取得字符串的長(zhǎng)度,但當(dāng)字符串是null的時(shí)候調(diào)用該字符串的Length()時(shí)候會(huì)拋出異常。下面我們?yōu)镾tring類添加一個(gè)自定義的方法LengthNullable(),如果字符串為null不拋出異常,而返回-1: 代碼如下: static class ExtensionMethods { public static int LengthNullable(this string test) { if (test != null) { return test.Length; } else { return -1 } } } C#3.0的擴(kuò)展方法需要單獨(dú)寫在一個(gè)public static的類中,并且擴(kuò)展方法也應(yīng)當(dāng)用public static修飾。擴(kuò)展方法的參數(shù)有三部份組成(this string test),第一部份是this關(guān)鍵字,它用來(lái)告訴編譯器該方法是擴(kuò)展方法;第二部份是該方法要追加到哪個(gè)類上,上面的例子代表該LengthNullable方法要追加到string類中去;第三部份是該類的實(shí)例名。 public static class ExtentionMethods { public static void Sleep(this Ren r) { Console.WriteLine(r.Name+" is sleeping....."); } } public class Ren { public string Name { get; set; } public int Age { get; set; } public void Speak() { Console.WriteLine(Name + Age); } } 如果擴(kuò)展方法與實(shí)例方法重名了,那在調(diào)用的時(shí)候只會(huì)調(diào)用到實(shí)例方法。
7.匿名方法 在C#2.0中就存在匿名方法,但很少有程序員去使用匿名方法,因?yàn)樗恼Z(yǔ)法有些怪異。匿名方法的主要用法:使用代理來(lái)替代一些簡(jiǎn)單的方法。 大家都知道代理是指向方法的指針。如: //聲明代理 delegate void Delegate(int x); //定義方法 void DoSomething(int y) { /* Something */ }; //把代理指向方法 Delegate d = obj.DoSomething; 這里的方法有方法名--DoSomething,而匿名方法可以讓代理直接指向一個(gè)沒(méi)有名子的方法。如: //聲明代理 delegate void Delegate(int x); //把代理指向一個(gè)匿名方法。 Del d = delegate(int y) { /* Do Something */ }; 匿名方法可以使代碼變得更緊湊、更清晰、占用更少的資源。由于匿名方法與代理的關(guān)系很密切,所以有的人也稱之為“匿名代理”。 在泛型集合List<T>中就有幾個(gè)方法Exists()、Find()、FindAll()、RemoveAll()等方法,在Array類中也有類似的方法。這些方法都能夠?qū)Ψ盒图线M(jìn)行簡(jiǎn)單的查詢操作,它們的方法簽名如下所示:
《圖3》 每個(gè)方法中都有個(gè)參數(shù)Predicate<T>,這個(gè)參數(shù)是個(gè)泛型代理,用來(lái)篩選數(shù)據(jù)。 如果不使用匿名方法,那我們得這樣編寫代碼: static bool HighUnitPrice(LineItem i) { if (i.UnitPrice > 25M) return true; else return false; } LineItem obj = Array.Find(LineItems, HighUnitPrice); 先定義一個(gè)方法HighUnitPrice(LineItem i),然后在Array.Find()的參數(shù)Predicate<T>中調(diào)用該方法。如果有了匿名方法就不用再單獨(dú)定義HighUnitPrice(LineItem i)方法了。 如: var anon = LineItemsList.Find( delegate(LineItem i) { return i.UnitPrice > = 25M; } ); 在C#3.0中的好多地方都用到了匿名方法,匿名方法是理解Lambda表達(dá)式的基礎(chǔ)。
8. Lambda表達(dá)式 Lambda表達(dá)式就是用很少的代碼來(lái)實(shí)現(xiàn)匿名方法。 語(yǔ)法格式: 參數(shù)列表=>表達(dá)式 下面我們來(lái)看看如何把匿名方法轉(zhuǎn)換為L(zhǎng)ambda表達(dá)式: 匿名方法: delegate(LineItem i) { return i.UnitPrice >= 25M; } 第一步:刪除關(guān)鍵字delegate,變?yōu)?/strong>(LineItem i) { return i.UnitPrice >= 25M; } 第二步:把花括號(hào){}替的換為L(zhǎng)ambda運(yùn)算符=>,變?yōu)?(LineItem i) => return i.UnitPrice >= 25M; 第三步:去掉return和分號(hào),語(yǔ)句變成表達(dá)式 (LineItem i) => i.UnitPrice >= 25M 第四步:由于編譯器會(huì)自動(dòng)進(jìn)行類型推斷,所以我們還可以把LineItem去掉。 i => i.UnitPrice >= 25M 這樣就把匿名方法變成了Lambda表達(dá)式。 var anon = LineItemsList.Find( i = > i.UnitPrice > = 25M ); 由此可見(jiàn)Lambda表達(dá)式是由Lambda運(yùn)算符分割開(kāi)的兩部份組成。右邊部分代表運(yùn)算的語(yǔ)句塊,左邊部分代表的是運(yùn)算需要的參數(shù)。 9.標(biāo)準(zhǔn)查詢操作(Standard Query Operators SQO) 標(biāo)準(zhǔn)查詢操作是對(duì)IEnumerable<T>接口追加的一系列的擴(kuò)展方法。通過(guò)這些擴(kuò)展方法對(duì)實(shí)現(xiàn)IEnumerable<T>接口的集合、數(shù)組進(jìn)行一系列的查詢操作。一些比較常用的擴(kuò)展方法有Where()/OrderBy()/Select()等,LinQ標(biāo)準(zhǔn)查詢操作我們將在后面詳細(xì)闡述。 LinQ中的from關(guān)鍵字并不是擴(kuò)展方法,因此它不是SQO。from只是為in關(guān)鍵字后的序列指定一個(gè)別名。 C#3.0的查詢表達(dá)式是以一個(gè)或多個(gè)“from 別名 in 序列名”子句開(kāi)始,以select或group子句結(jié)束。而join/let/where/orderby等子句是可選的,需要寫在from和select/group子句中間。 下面是對(duì)內(nèi)存中List<productList>集合進(jìn)行查詢的例子 var noStock = from p in productList where p.UnitsInStock == 0 orderby p.Category, p.ProductID select new { p.ProductID, p.Category, p.ProductName }; 編譯器會(huì)自動(dòng)把上面的表達(dá)式語(yǔ)句翻譯成下面的鏈?zhǔn)椒椒ㄕ{(diào)用,在鏈?zhǔn)椒椒ㄕ{(diào)用中使用的就是lambda表達(dá)式 var noStock = productList .Where(p = > p.UnitsInStock == 0) .OrderBy(p = > p.Category) .ThenBy(p = > p.ProductID) .Select(p = > new { p.ProductID, p.Category, p.ProductName }); 10.IQueryable<T>接口 IQueryable<T>類型不是集合,他們是支持多態(tài)、優(yōu)化、動(dòng)態(tài)查詢功能的LinQ查詢序列,它能夠把標(biāo)準(zhǔn)化查詢操作(SQO)轉(zhuǎn)換成表達(dá)式樹(shù)。簡(jiǎn)而言之,IQueryable<T>接收的對(duì)象不是集合,而是查詢表達(dá)式樹(shù)。 我們可以使用IQueryable<T>的ToList()方法來(lái)把查詢序列變成List<T>類型,使用ToArray()方法把查詢序列變成數(shù)組類型。 IEnumerable<T>接口中有個(gè)AsQueryable()方法,該方法返回的是也IQueryable<T>類型。 (原創(chuàng):灰灰蟲(chóng)的家http://hi.baidu.com/grayworm)
|