LinQ家族五大成員:
LinQ to Objects - 預設功能,用來實作對記憶體中集合對象的查詢
LinQ to SQL - 針對SQL Server的查詢,它是一個帶有可視化的操作界面的ORM工具
LinQ to DataSet - 對強類型化或弱類型化的DataSet或獨立的DataTable進行查詢
LinQ to Entity - 對實體架構中EDM定義的實體集合進行查詢。
LinQ to XML - 對XML文檔進行查詢建立等操作。
C#文法與LinQ相關的新增功能
1.隐式強類型變量
在C#3.0中可以使用var關鍵字隐式定義強類型局部變量。

《圖1》
這裡的var關鍵字定義變量與JavaScript定義變量看起來很像但二者有着本質的差別。
JavaScript定義的變量是弱類型的變量,也可了解為是一種通用類型的變量,它可以容納各種類型的值,還可以在運作過程中動态修改其中的内容類型。下面這種寫法在JavaScript中是正确的:
var obj = 3.14;
obj = "hello world";
C#中的var則是種強類型的變量,它在定義的時候會确定資料類型,配置設定記憶體空間。上面這兩名代碼在C#3.0中會報錯,因為第一句已經把obj定義為double型的變量,第二句把字元串指派給double是錯誤的操作。
隐式強類型并不能有效簡化我的書寫的代碼,但當我們在用它來動态接收一些未知類型的資料的時候就顯雖得很強大。在隐式強類型變量出現前,我們一般是使用object型變量來接收這種未知類型的資料的。
2.對象初始化
這個功能可以有效簡化類的getter和setter部份的代碼,還可以隻使用一行表達式語句實作對象的執行個體化與初始化操作。
如定義執行個體類時可以使用如下代碼,沒有必要再把成員變量和屬性分别定義了。
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; }
}
執行個體化LineItem對象,并為它指派
var line3 = new LineItem { OrderID = 11000, ProductID = 61, Quantity = 30, QuantityPerUnit = “12 1-kg cartons”, UnitPrice = 15.55M, Discount = 0.15F };
3.數組初始化
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 >
QuantityPerUnit = “24 500-g bottles”, UnitPrice = 15.55M, Discount = 0.0F},
QuantityPerUnit = “12 1-kg cartons”, UnitPrice = 20.2M, Discount = 0.1F},
QuantityPerUnit = “24 1-kg bags”, UnitPrice = 25.45M, Discount = 0.15F}
5.匿名類型
過去我們要生成對象時,必須事先定義該對象的類,然後使用new關鍵字來執行個體化該類。匿名類型簡化定義類的這個過程,我們可以使用new關鍵字直接把類的定義,類的執行個體化放在一個表達式語句中。
如:
var obj = new
Name = "zhangsan",
Age = 18,
Console.WriteLine(obj.Name + obj.Age + obj.URL);
匿名類型主要用在LinQ to SQL中對字段的投影功能上:
var query = from i in LineItems select new { i.OrderID, i.ProductID, i.UnitPrice }
6.擴充方法
擴充方法就是為現有的類追加我們自定義的方法。在C#3.0的內建開發環境中,我們會發現帶有向下箭頭的方法,這些方法是我們在C#2.0中所沒有見到的方法,這些方法就是我們所謂的“擴充方法”,它是C#3.0在C#2.0的基礎上新增的一系列的方法,當然我們也可以為内置類添加我們自己的主擴充方法。
《圖2》
例如在string中有個Length()擴充方法,它用來取得字元串的長度,但當字元串是null的時候調用該字元串的Length()時候會抛出異常。下面我們為String類添加一個自定義的方法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的擴充方法需要單獨寫在一個public static的類中,并且擴充方法也應當用public static修飾。擴充方法的參數有三部份組成(this string test),第一部份是this關鍵字,它用來告訴編譯器該方法是擴充方法;第二部份是該方法要追加到哪個類上,上面的例子代表該LengthNullable方法要追加到string類中去;第三部份是該類的執行個體名。
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);
如果擴充方法與執行個體方法重名了,那在調用的時候隻會調用到執行個體方法。
7.匿名方法
在C#2.0中就存在匿名方法,但很少有程式員去使用匿名方法,因為它的文法有些怪異。匿名方法的主要用法:使用代理來替代一些簡單的方法。
大家都知道代理是指向方法的指針。如:
//聲明代理
delegate void Delegate(int x);
//定義方法
void DoSomething(int y) { /* Something */ };
//把代理指向方法
Delegate d = obj.DoSomething;
這裡的方法有方法名--DoSomething,而匿名方法可以讓代理直接指向一個沒有名子的方法。如:
delegate void Delegate(int x);
//把代理指向一個匿名方法。
Del d = delegate(int y) { /* Do Something */ };
匿名方法可以使代碼變得更緊湊、更清晰、占用更少的資源。由于匿名方法與代理的關系很密切,是以有的人也稱之為“匿名代理”。
在泛型集合List<T>中就有幾個方法Exists()、Find()、FindAll()、RemoveAll()等方法,在Array類中也有類似的方法。這些方法都能夠對泛型集合進行簡單的查詢操作,它們的方法簽名如下所示:
《圖3》
每個方法中都有個參數Predicate<T>,這個參數是個泛型代理,用來篩選資料。
如果不使用匿名方法,那我們得這樣編寫代碼:
static bool HighUnitPrice(LineItem i)
if (i.UnitPrice > 25M)
return true;
else
return false;
LineItem obj = Array.Find(LineItems, HighUnitPrice);
先定義一個方法HighUnitPrice(LineItem i),然後在Array.Find()的參數Predicate<T>中調用該方法。如果有了匿名方法就不用再單獨定義HighUnitPrice(LineItem i)方法了。
var anon = LineItemsList.Find(
delegate(LineItem i)
return i.UnitPrice > = 25M;
);
在C#3.0中的好多地方都用到了匿名方法,匿名方法是了解Lambda表達式的基礎。
8. Lambda表達式
Lambda表達式就是用很少的代碼來實作匿名方法。
文法格式:
參數清單=>表達式
下面我們來看看如何把匿名方法轉換為Lambda表達式:
匿名方法:
delegate(LineItem i) { return i.UnitPrice >= 25M; }
第一步:删除關鍵字delegate,變為(LineItem i) { return i.UnitPrice >= 25M; }
第二步:把花括号{}替的換為Lambda運算符=>,變為 (LineItem i) => return i.UnitPrice >= 25M;
第三步:去掉return和分号,語句變成表達式 (LineItem i) => i.UnitPrice >= 25M
第四步:由于編譯器會自動進行類型推斷,是以我們還可以把LineItem去掉。 i => i.UnitPrice >= 25M
這樣就把匿名方法變成了Lambda表達式。
var anon = LineItemsList.Find( i = > i.UnitPrice > = 25M );
由此可見Lambda表達式是由Lambda運算符分割開的兩部份組成。右邊部分代表運算的語句塊,左邊部分代表的是運算需要的參數。
9.标準查詢操作(Standard Query Operators SQO)
标準查詢操作是對IEnumerable<T>接口追加的一系列的擴充方法。通過這些擴充方法對實作IEnumerable<T>接口的集合、數組進行一系列的查詢操作。一些比較常用的擴充方法有Where()/OrderBy()/Select()等,LinQ标準查詢操作我們将在後面詳細闡述。
LinQ中的from關鍵字并不是擴充方法,是以它不是SQO。from隻是為in關鍵字後的序列指定一個别名。
C#3.0的查詢表達式是以一個或多個“from 别名 in 序列名”子句開始,以select或group子句結束。而join/let/where/orderby等子句是可選的,需要寫在from和select/group子句中間。
下面是對記憶體中List<productList>集合進行查詢的例子
var noStock = from p in productList
where p.UnitsInStock == 0
orderby p.Category, p.ProductID
select new { p.ProductID, p.Category, p.ProductName };
編譯器會自動把上面的表達式語句翻譯成下面的鍊式方法調用,在鍊式方法調用中使用的就是lambda表達式
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>類型不是集合,他們是支援多态、優化、動态查詢功能的LinQ查詢序列,它能夠把标準化查詢操作(SQO)轉換成表達式樹。簡而言之,IQueryable<T>接收的對象不是集合,而是查詢表達式樹。
我們可以使用IQueryable<T>的ToList()方法來把查詢序列變成List<T>類型,使用ToArray()方法把查詢序列變成數組類型。
IEnumerable<T>接口中有個AsQueryable()方法,該方法傳回的是也IQueryable<T>類型。