Select操做符對單個序列或集合中的值進行投影。下面的示例中使用select從序列中返回Employee表的全部列:html
using (NorthwindDataContext db=new NorthwindDataContext())數組 {ide //查詢語法函數 var query =this from e in db.Employeesspa where e.FirstName.StartsWith("M")code select e;orm //方法語法htm var q =對象 db.Employees .Where(e => e.FirstName.StartsWith("M")) .Select(e => e);
foreach (var item in query) { Console.WriteLine(item.FirstName); } } |
固然,你也能夠返回單個列,例如:
var query = from e in db.Employees where e.FirstName.StartsWith("M") select e.FirstName; |
你也能夠返回序列中的某幾列,例如:
var query = from e in db.Employees where e.FirstName.StartsWith("M") select new { e.FirstName, e.LastName, e.Title }; |
SelectMany操做符提供了將多個from子句組合起來的功能,它將每一個對象的結果合併成單個序列。下面是一個示例:
using (NorthwindDataContext db=new NorthwindDataContext()) { //查詢語法 var query = from e in db.Employees from o in e.Orders select o;
//方法語法 var q = db.Employees .SelectMany(e => e.Orders);
foreach (var item in query) { Console.WriteLine(item.Freight); } } |
Where是限制操做符,它將過濾標準應用在序列上,按照提供的邏輯對序列中的數據進行過濾。
Where操做符不啓動查詢的執行。當開始對序列進行遍歷時查詢纔開始執行,此時過濾條件將被應用到查詢中。Where操做符的使用方法已經在第一節中出現過,這裏再也不冗述。
排序操做符,包括OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse,提供了升序或者降序排序。
OrderBy操做符將序列中的元素按照升序排列。下面的示例演示了這一點:
using (NorthwindDataContext db = new NorthwindDataContext()) { //查詢語法 var query = from e in db.Employees orderby e.FirstName select e;
//方法語法 var q = db.Employees .OrderBy(e => e.FirstName) .Select(e => e);
foreach (var item in q) { Console.WriteLine(item.FirstName); } } |
這裏可使用OrderBy的重載方法OrderBy(Func<T,TKey>,IComparer<Tkey>)來指定序列的排序方式。
OrderByDescending操做符將序列中的元素按照降序排列。用法與OrderBy相同,這裏再也不演示。
ThenBy操做符實現按照次關鍵字對序列進行升序排列。此操做符的查詢語法與方法語法略有不一樣,如下代碼演示了這一點:
using (NorthwindDataContext db = new NorthwindDataContext()) { //查詢語法 var query = from e in db.Employees orderby e.FirstName,e.LastName select e;
//方法語法 var q = db.Employees .OrderBy(e => e.FirstName) .ThenBy(e => e.LastName) .Select(e => e);
foreach (var item in query) { Console.WriteLine(item.FirstName); } } |
ThenByDescending操做符實現按照次關鍵字對序列進行降序排列。此操做符的查詢語法與方法語法略有不一樣,如下代碼演示了這一點:
using (NorthwindDataContext db = new NorthwindDataContext()) { //查詢語法 var query = from e in db.Employees orderby e.FirstName,e.LastName descending select e;
//方法語法 var q = db.Employees .OrderBy(e => e.FirstName) .ThenByDescending(e => e.LastName) .Select(e => e);
foreach (var item in query) { Console.WriteLine(item.FirstName); } } |
Reverse將會把序列中的元素按照從後到前的循序反轉。須要注意的是,Reverse方法的返回值是void,如下代碼演示了這一點:
using (NorthwindDataContext db = new NorthwindDataContext()) { //方法語法 var q = db.Employees .Select(e => e.FirstName) .ToList(); q.Reverse();
foreach (var item in q) { Console.WriteLine(item); } } |
聯接是指將一個數據源對象與另外一個數據源對象進行關聯或者聯合的操做。這兩個數據源對象經過一個共同的值或者屬性進行關聯。
LINQ有兩個聯接操做符:Join和GroupJoin。
Join操做符相似於T-SQL中的inner join,它將兩個數據源相聯接,根據兩個數據源中相等的值進行匹配。例如,能夠將產品表與產品類別表相聯接,獲得產品名稱和與其相對應的類別名稱。如下的代碼演示了這一點:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //查詢語法 var query = from p in db.Products join c in db.Categories on p.CategoryID equals c.CategoryID where p.CategoryID == 1 select p;
//方法語法 var q = db.Products .Join ( db.Categories, p => p.CategoryID, c => c.CategoryID, (p, c) => p ) .Where(p => p.CategoryID == 1);
foreach (var item in query) { Console.WriteLine(item.ProductName); } } |
以上代碼爲表述清晰加入了一個條件「where p.CategoryID == 1」,即僅返回產品類別ID爲1的全部產品。
GroupJoin操做符常應用於返回「主鍵對象-外鍵對象集合」形式的查詢,例如「產品類別-此類別下的全部產品」。如下的代碼演示了這一點:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //查詢語法 var query = from c in db.Categories join p in db.Products on c.CategoryID equals p.CategoryID into r select new { c.CategoryName, Products = r };
//方法語法 var q = db.Categories .GroupJoin ( db.Products, c => c.CategoryID, p => p.CategoryID, (c, p) => new { c.CategoryName, Products = p } );
foreach (var item in query) { Console.WriteLine("{0} =>", item.CategoryName); foreach (var p in item.Products) { Console.WriteLine(p.ProductName); } Console.WriteLine("----------------------------------------------"); } } |
分組是根據一個特定的值將序列中的元素進行分組。LINQ只包含一個分組操做符:GroupBy。
下面的示例中使用了產品表,以CategoryID做爲分組關鍵值,按照產品類別對產品進行了分組。
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //查詢語法 var query = from p in db.Products group p by p.CategoryID;
//方法語法 var q = db.Products .GroupBy(p => p.CategoryID);
foreach (var item in query) { Console.WriteLine("{0} =>", item.Key); foreach (var p in item) { Console.WriteLine(p.ProductName); } Console.WriteLine("----------------------------------------------"); } } |
執行GroupBy獲得的序列中包含的元素類型爲IGrouping<TKey?, T>,其Key屬性表明了分組時使用的關鍵值,遍歷IGrouping<TKey?, T>元素能夠讀取到每個T類型。在此示例中,對應的元素類型爲IGrouping<int?, Products>,其Key屬性即爲類別ID,遍歷它能夠讀取到每個產品對象。
串聯是一個將兩個集合聯接在一塊兒的過程。在LINQ中,這個過程經過Concat操做符來實現。
在下面的示例中,將會把類別名稱串聯在產品名稱以後:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中
//方法語法 var q = db.Products .Select(p => p.ProductName) .Concat ( db.Categories.Select(c => c.CategoryName) );
foreach (var item in q) { Console.WriteLine(item); } } |
聚合函數將在序列上執行特定的計算,並返回單個值,如計算給定序列平均值、最大值等。共有7種LINQ聚合查詢操做符:Aggregate、Average、Count、LongCount、Max、Min和Sum。
Aggregate操做符對集合值執行自定義聚合運算。例如,須要列出全部產品類別清單,每一個類別名稱之間用頓號鏈接。如下的代碼演示了這一過程:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中
//方法語法 var q = db.Categories .Select(c => c.CategoryName) .ToArray() .Aggregate((current, next) => String.Format("{0}、{1}", current, next));
Console.WriteLine(q); } |
若是你對這一過程有些迷惑,那麼請參照如下代碼:
var query = db.Categories .Select(c => c.CategoryName) .ToArray(); string r = String.Empty; foreach (var item in query) { r += "、"; r += item; } r = r.Substring(1); //去除第一個頓號 Console.WriteLine(r); |
求集合中元素的平均值,返回值類型double
求集合中元素的個數,返回值類型Int32
求集合中元素的個數,返回值類型Int64
求集合中元素的最大值
求集合中元素的最小值
求集合中元素的和
LINQ 中的集合操做符是指根據相同或不一樣集合(或集)中是否存在等效元素來生成結果集的查詢操做,一共有4種:
方法名 |
說明 |
Distinct |
從集合移除重複值。 |
Except |
返回差集,差集是指位於一個集合但不位於另外一個集合的元素。 |
Intersect |
返回交集,交集是指同時出如今兩個集合中的元素。 |
Union |
返回並集,並集是指位於兩個集合中任一集合的惟一的元素。 |
使用方式均爲「集合1.方法名(集合2)」,返回值爲運算結果的集合,這裏就不演示了。
生成是指建立新的值序列。
Empty操做符返回一個指定類型的空集合。這裏的空不是null,而是元素數量爲0的集合。如下的示例演示瞭如何建立一個IEnumerable<int>類型的空集合:
var q = Enumerable.Empty<int>(); Console.WriteLine(q == null); Console.WriteLine(q.Count()); |
DefaultIfEmpty將空集合替換爲具備默認值的單一實例集合。執行此方法得到的集合將至少含有一個元素,這是由於DefaultIfEmpty方法須要兩個參數,第一個參數是一個泛型集合,第二個參數是相應類型的單個元素,若是第一個參數中不含有任何元素,它將返回第二個參數指定的單個元素。若是你使用了DefaultIfEmpty方法的重載方法DefaultIfEmpty<T>(IEnumerable<T> array),若是指定的array集合爲空,那麼將返回一個類型爲T,值爲null的單個對象。如下的代碼演示了這一過程:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中
//方法語法 var q = Enumerable.DefaultIfEmpty ( db.Employees .Where(e => e.FirstName.StartsWith("Aaf")) //更改此處的條件可得到不一樣的集合 , new Employees() { FirstName = "Sunny D.D" } ); Console.WriteLine(q.Count()); foreach (var item in q) { Console.WriteLine(item.FirstName); } } |
Range操做符用於生成指定範圍內的整數的序列。它須要兩個參數,第一個參數是序列開始的整數值,第二個參數是序列中整數的數量。下面的示例演示了使用Range操做符來生成從0到9的整數序列:
var q = Enumerable.Range(0, 10); foreach (var item in q) { Console.WriteLine(item); } |
Repeat操做符用於生成包含一個重複值的集合。它須要兩個參數,第一個參數是任意類型的元素,第二個參數是生成的序列中所包含此元素的數量。下面的示例演示了使用Repeat來生成一個包含10個0的序列:
var q = Enumerable.Repeat(0, 10); foreach (var item in q) { Console.WriteLine(item); } |
轉換操做符是用來實現將輸入對象的類型轉變爲序列的功能。名稱以「As」開頭的轉換方法可更改源集合的靜態類型但不枚舉(延遲加載)此源集合。名稱以「To」開頭的方法可枚舉(即時加載)源集合並將項放入相應的集合類型。
全部實現了IEnumerable<T>接口的類型均可以調用此方法來獲取一個IEnumerable<T>集合。此方法通常僅用於實現類中的方法與IEnumerable<T>接口方法重名時。例如,實現類Test中有一個Where方法,當使用Test對象調用Where時,將執行Test自身的Where方法過程。若是要執行IEnumerable<T>的Where方法,即可以使用AsEnumerable進行進行轉換後,再調用Where方法便可。固然,將實現類Test隱式轉換爲IEnumerable<T>接口,再調用接口的Where方法也能達到一樣的效果。如下的代碼演示了這一過程:
class AsEnumerableTest<T> : List<T> { public void Where(Func<T, bool> func) { Console.WriteLine("AsEnumerableTest的Where方法"); } } public static void AsEnumerable() { AsEnumerableTest<int> q = new AsEnumerableTest<int>() { 1,2,3,4 }; q.Where(r => r < 3);
//q.AsEnumerable().Where(r => r < 3);
//IEnumerable<int> i = q; //i.Where(r => r < 3); } |
Cast<T> 方法經過提供必要的類型信息,可在IEnumerable(非泛型)的派生對象上調用Cast<T> 方法來得到一個IEnumerable<T>對象。例如,ArrayList 並不實現IEnumerable<T>,但經過調用 ArrayList 對象上的 Cast<T>(),就可使用標準查詢運算符查詢該序列。
若是集合中的元素沒法強制轉換爲 T 類型,則此方法將引起異常。如下代碼演示了這一過程:
ArrayList array = new ArrayList(); array.Add("Bob"); array.Add("Jack"); array.Add(1); foreach (var item in array.Cast<string>()) { Console.WriteLine(item); } |
運行此代碼,能夠輸出「Bob」、「Jack」,而後會報出一個異常「沒法將int強制轉換爲string」,這說明Cast方法也是延遲執行實現的,只有在枚舉過程當中纔將對象逐個強制轉換爲T類型。
OfType <T> 方法經過提供必要的類型信息,可在IEnumerable(非泛型)的派生對象上調用OfType <T> 方法來得到一個IEnumerable<T>對象。執行OfType<T>方法將返回集合中強制轉換類型成功的全部元素。也就是說,OfType<T>方法與Cast<T> 方法的區別在於,若是集合中的元素在強制轉換失敗的時候會跳過,而不是拋出異常。
ToArray 操做符能夠在IEnumerable<T> 類型的任何派生對象上調用,返回值爲T類型的數組。
ToDictionary操做符根據指定的鍵選擇器函數,從IEnumerable<T>建立一個Dictionary<TKey, TValue>。下面的示例中,將查詢到的產品類別集合轉換爲Dictionary<類別ID,類別名稱>的鍵-值集合:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //方法語法 var q = db.Categories .ToDictionary ( c => c.CategoryID, c => c.CategoryName );
foreach (var item in q) { Console.WriteLine("{0} - {1}",item.Key,item.Value); } } |
須要注意的是,若是省略ToDictionary方法的第二個參數(值選擇函數),那麼Value將會保存一個類別對象。還有,若是Key爲null,或者出現重複的Key,都將致使拋出異常。
ToList操做符能夠在IEnumerable<T> 類型的任何派生對象上調用,返回值爲List<T>類型的對象。
ToLookup操做符將建立一個 Lookup<TKey, TElement>對象,這是一個one-to-many集合,一個Key能夠對應多個Value。如下的示例以產品表的全部數據做爲數據源,以類別ID做爲Key調用了ToLookup方法,而後遍歷返回的Lookup<TKey, TElement>對象,輸出了類別ID以及此類別下的全部產品名稱:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //方法語法 var q = db.Products .ToLookup ( p => p.CategoryID, p => p.ProductName );
foreach (var item in q) { Console.WriteLine(item.Key); foreach (var p in item) { Console.WriteLine(p); } } } |
能夠看出,ToLookup操做與GroupBy操做很類似,只不過GroupBy是延遲加載的,而ToLookup是即便加載。
元素操做符將從一個序列中返回單個指定的元素。
First操做將返回序列中的第一個元素。若是序列中不包含任何元素,則First<T>方法將引起異常。若要在源序列爲空時返回默認值,須要使用FirstOrDefault方法。如下代碼演示了First<T>方法的使用方式:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //無參 var query = db.Employees .First();
//有參 var q = db.Employees .First(e => e.FirstName.StartsWith("S"));
Console.WriteLine(q.FirstName); } |
上述代碼中使用了First<T>方法的無參方式與有參方式。First<T>的有參方式中能夠指定一個條件,操做將返回序列中知足此條件的第一個元素。從查詢結果上看,source.First<T>(條件)方法與source.Where(條件).First<T>()是同樣的,可是須要注意「First<T>(條件)操做將返回序列中知足此條件的第一個元素」,這將忽略後面的遍歷操做,效率更高。
FirstOrDefault方法將返回序列中的第一個元素;若是序列中不包含任何元素,則返回默認值。它也能夠像First方法同樣傳遞一個條件。須要說明的是若是序列中不包含任何元素,返回的默認值是個怎樣的元素。在這以前,先來看一下FirstOrDefault方法是如何實現的:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source) { if (source == null) { throw Error.ArgumentNull("source"); } IList<TSource> list = source as IList<TSource>; if (list != null) { if (list.Count > 0) { return list[0]; } } else { using (IEnumerator<TSource> enumerator = source.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } } return default(TSource); } |
如下給出MSDN中,對於default(T)關鍵字的描述:
在泛型類和泛型方法中產生的一個問題是,在預先未知如下狀況時,如何將默認值分配給參數化類型 T:
給定參數化類型 T 的一個變量 t,只有當 T 爲引用類型時,語句 t = null 纔有效;只有當 T 爲數值類型而不是結構時,語句 t = 0 才能正常使用。解決方案是使用 default 關鍵字,此關鍵字對於引用類型會返回 null,對於數值類型會返回零。對於結構,此關鍵字將返回初始化爲零或 null 的每一個結構成員,具體取決於這些結構是值類型仍是引用類型。 |
Last方法將返回序列中的最後一個元素。使用方法參照First。
LastOrDefault方法將返回序列中的最後一個元素;若是序列中不包含任何元素,則返回默認值。使用方法參照FirstOrDefault。
ElementAt方法返回序列中指定索引處的元素。使用方法參照First。須要注意的是若是索引超出範圍會致使異常。
ElementAtOrDefault方法將返回序列中指定索引處的元素;若是索引超出範圍,則返回默認值。使用方法參照FirstOrDefault。
Single方法的無參形式將從一個序列中返回單個元素,若是該序列包含多個元素,或者沒有元素數爲0,則會引起異常。也就是說,在序列執行Single方法的無參形式時,必須保證該序列有且僅有一個元素。
Single方法的有參形式將從一個序列中返回符合指定條件的惟一元素,若是有多個元素,或者沒有元素符合這一條件,則會引起異常。如下代碼演示了Single的使用方式:
using (NorthwindDataContext db = new NorthwindDataContext()) { db.Log = Console.Out; //將生成的T-SQL語句輸出到控制檯中 //方法語法 var q = db.Employees .Single();
var query = db.Employees .Single(e => e.FirstName.StartsWith("S")); Console.WriteLine(query.FirstName); } |
SingleOrDefault方法的無參形式將從一個序列中返回單個元素。若是元素數爲0,則返回默認值。若是該序列包含多個元素,則會引起異常。
SingleOrDefault方法的有參形式將從一個序列中返回符合指定條件的惟一元素,若是元素數爲0,則返回默認值;若是該序列包含多個元素,則會引起異常。SingleOrDefault的使用方式與Single相同。
須要注意的是,Single方法與SingleOrDefault方法都是即時加載的,在代碼進行到方法所在位置時,若是引起了異常,會馬上拋出。
若是兩個序列的對應元素相等且這兩個序列具備相同數量的元素,則視這兩個序列相等。
SequenceEqual方法經過並行地枚舉兩個數據源並比較相應元素來判斷兩個序列是否相等。若是兩個序列徹底相等,返回true,不然返回false。如下代碼是SequenceEqual方法的實現過程:
public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second,IEqualityComparer<TSource> comparer) { if (comparer == null) { comparer = EqualityComparer<TSource>.Default; } if (first == null) { throw Error.ArgumentNull("first"); } if (second == null) { throw Error.ArgumentNull("second"); } using (IEnumerator<TSource> enumerator = first.GetEnumerator()) { using (IEnumerator<TSource> enumerator2 = second.GetEnumerator()) { while (enumerator.MoveNext()) { if (!enumerator2.MoveNext() || !comparer.Equals(enumerator.Current, enumerator2.Current)) { return false; } } if (enumerator2.MoveNext()) { return false; } } } return true; } |
以上代碼的執行過程爲:
限定符運算返回一個 Boolean 值,該值指示序列中是否有一些元素知足條件或是否全部元素都知足條件。
下圖描述了兩個不一樣源序列上的兩個不一樣限定符運算。第一個運算詢問是否有一個或多個元素爲字符「A」,結果爲 true。第二個運算詢問是否全部元素都爲字符「A」,結果爲true。
All方法用來肯定是否序列中的全部元素都知足條件。如下代碼演示了All的用法:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" }; string[] source2 = new string[] { "A", "A", "A", "A", "A", "A" };
Console.WriteLine(source1.All(w => w == "A")); //console will print "False" Console.WriteLine(source2.All(w => w == "A")); //console will print "True" |
Any方法的無參方式用來肯定序列是否包含任何元素。若是源序列包含元素,則爲 true;不然爲 false。
Any方法的有參方式用來肯定序列中是否有元素知足條件。只要有一個元素符合指定條件即返回true,若是一個符合指定條件的元素都沒有則返回false。如下代碼演示了Any方法有參方式的用法:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" }; string[] source2 = new string[] { "A", "A", "A", "A", "A", "A" };
Console.WriteLine(source1.Any(w => w == "A")); //console will print "True" Console.WriteLine(source2.Any(w => w == "A")); //console will print "True" |
Contains方法用來肯定序列是否包含知足指定條件的元素。若是有返回true,不然返回false。如下代碼使用默認的String比較器來判斷序列中是否含有指定的字符串:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" };
Console.WriteLine(source1.Contains("A")); //console will print "True" Console.WriteLine(source1.Contains("G")); //console will print "False" |
若是要對序列中的元素進行自定義比較,須要一個IEqualityComparer<T>接口的實現類做爲比較器,用於比較序列中的元素。
LINQ 中的分區指的是在不從新排列元素的狀況下,將輸入序列劃分爲兩部分,而後返回其中一個部分的操做。
下圖顯示對一個字符序列執行三個不一樣的分區操做的結果。第一個操做返回序列中的前三個元素。第二個操做跳過前三個元素,返回剩餘的元素。第三個操做跳過序列中的前兩個元素,返回接下來的三個元素。
Take(int n)方法將從序列的開頭返回數量爲n的連續元素。如下代碼演示了從一個序列中返回其前五個元素:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.Take(5); foreach (var item in q) { Console.WriteLine(item); } |
上述代碼的運行結果爲下圖所示:
TakeWhile方法執行時將逐個比較序列中的每一個元素是否知足指定條件,直到碰到不符合指定的條件的元素時,返回前面全部的元素組成的序列。如下代碼演示了這一過程:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.TakeWhile(i => i < 100); foreach (var item in q) { Console.WriteLine(item); } |
上述代碼的運行結果爲下圖所示:
Skip(int n)方法將跳過序列開頭的n個元素,而後返回其他的連續元素。如下代碼演示了從一個序列中跳過前五個元素,而後返回其他的元素組成的序列:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.Skip(5); foreach (var item in q) { Console.WriteLine(item); } |
上述代碼的運行結果爲下圖所示:
SkipWhile方法執行時將逐個比較序列中的每一個元素是否知足指定條件,直到碰到不符合指定的條件的元素時,返回其他全部的元素組成的序列。如下代碼演示了這一過程:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 }; var q = source.SkipWhile(i => i < 100); foreach (var item in q) { Console.WriteLine(item); } |
上述代碼的運行結果爲下圖所示:
本文介紹了LINQ標準查詢操做符。沒有這些操做符,LINQ就不會存在。本文爲理解這些操做符的功能提供了很好的基礎。瞭解它們將會頗有幫助,由於LINQ的各類Provider都是基於這些操做符來完成各自豐富的功能。