應(yīng)用程序還需要操作存儲(chǔ)在其他數(shù)據(jù)源(如SQL數(shù)據(jù)庫或XML文件)中的數(shù)據(jù)淮菠,甚至通過Web服務(wù)訪問它們稽煤。傳統(tǒng)上,查詢這些數(shù)據(jù)源時(shí)需要使用不同的語法榨婆,且在編譯期間不進(jìn)行類型檢查。
一褒侧、LINQ
在.NET Framework中良风,查詢表達(dá)式是一組統(tǒng)稱為語言集成查詢(LINQ)的技術(shù)的一部分谊迄,將查詢功能直接集成到了C#語言中。LINQ是.NET Framework 3.5新增的烟央,它提供了適用于所有數(shù)據(jù)源(SQL數(shù)據(jù)庫统诺、XML文檔、Web服務(wù)疑俭、ADO.NET數(shù)據(jù)庫以及任何支持接口IEnumberable或IEnumerable<T>的集合)的查詢語言粮呢,從而避免了操作數(shù)據(jù)和對(duì)象時(shí)經(jīng)常出現(xiàn)的語言不匹配問題。
LINQ讓查詢變成了基本語言構(gòu)造钞艇,就像算術(shù)運(yùn)算和流程控制語句是C#基本概念一樣啄寡。LINQ 查詢將重點(diǎn)放在常用的操作而不是數(shù)據(jù)結(jié)構(gòu)上,能夠以一致的方式從任何支持的數(shù)據(jù)源檢索數(shù)據(jù)哩照,并對(duì)其進(jìn)行轉(zhuǎn)換挺物。
與SQL(Structured Query Language,結(jié)構(gòu)化查詢語言)查詢相比飘弧,LINQ查詢的語法相同识藤,使用的一些關(guān)鍵字相同,提供的很多優(yōu)點(diǎn)也相同眯牧。您可隨便修改被查詢的底層數(shù)據(jù)的結(jié)構(gòu)蹋岩,而不會(huì)導(dǎo)致需要修改查詢。SQL只能用于操作關(guān)系型數(shù)據(jù)学少,而LINQ支持的數(shù)據(jù)結(jié)構(gòu)要多得多剪个。
如下代碼是一個(gè)用于Contact對(duì)象集合的查詢
class Contact
{
public int Id { get; set; }
public string Company { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
}
IEnumerable<Contact> contacts = GetContacts();
var result = from contact in contacts select contact.FirstName;
foreach(var name in result)
{
Console.WriteLine(name);
}
這個(gè)簡(jiǎn)單的查詢演示了 C#語言支持的聲明性語法(declartive syntax,也叫 query comprehension syntax)版确。這種語法讓您能夠使用類似于SQL查詢的語法編寫查詢扣囊,靈活性和表達(dá)能力都極強(qiáng)。雖然查詢表達(dá)式中所有的變量都是強(qiáng)類型的绒疗,但是在大多數(shù)情況下侵歇,不需要顯式地指定類型,因?yàn)榫幾g器能夠推斷出來吓蘑。
ps:LINQ查詢語法
如果您熟悉SQL惕虑,就不會(huì)對(duì)LINQ使用的查詢語法感到陌生。最明顯的差別是磨镶,from運(yùn)算符位于select運(yùn)算符的前面溃蔫,而不像SQL中那樣位于后面。
1.1 選擇數(shù)據(jù)
雖然上述所示的代碼看起來可能很簡(jiǎn)單琳猫,但是實(shí)際上涉及的內(nèi)容很多伟叛。首先應(yīng)注意到的是,使用了一個(gè)類型將隱式確定的變量result脐嫂,其類型實(shí)際上是IEnumerable<string>统刮。查詢表達(dá)式(賦值運(yùn)算符右邊的代碼)的結(jié)果為查詢紊遵,而不是查詢的結(jié)果。select 子句返回一個(gè)對(duì)象侥蒙,該對(duì)象表示對(duì)一個(gè)序列( contacts 列表)執(zhí)行投影操作的結(jié)果(一系列contact.FirstName值)暗膜。由于結(jié)果是一系列字符串,因此result必然是由字符串組成的可枚舉集合辉哥。當(dāng)前桦山,并不會(huì)檢索數(shù)據(jù),而只是返回一個(gè)可枚舉集合醋旦,以后再將數(shù)據(jù)取回恒水。
這個(gè)查詢相當(dāng)于說,從contacts指定的數(shù)據(jù)源中饲齐,選擇每個(gè)元素(contact)的FirstName字段钉凌。from子句中指定的變量contact類似于foreach語句中的迭代變量,它是一個(gè)只讀局部變量捂人,作用域?yàn)椴樵儽磉_(dá)式御雕。in子句指定了要從中查詢?cè)氐臄?shù)據(jù)源,而select子句指定在迭代期間只選擇每個(gè)元素的contact.FirstName字段滥搭。
選擇單個(gè)字段時(shí)酸纲,這種語法的效果很好,但通常需要選擇多個(gè)字段瑟匆,甚至以某種方式對(duì)數(shù)據(jù)進(jìn)行變換闽坡,如合并字段。所幸的是愁溜,LINQ通過類似的語法提供了這樣的支持疾嗅。實(shí)際上,有多種方法執(zhí)行這些類型的選擇冕象。
第一種方法是在select子句中拼接這些字段代承,這樣將只返回一個(gè)字段,如下代碼所示:
var result = from contact in contacts select contact.FirstName + " " + contact.LastName;
foreach(var name in result)
{
Console.WriteLine(name);
}
顯然渐扮,這種選擇方式只適用于有限的情形论悴。一種更靈活的方法是返回多個(gè)字段,即返回一個(gè)數(shù)據(jù)子集墓律,如下所示:
var result = from contact in contacts
select new
{
Name = contact.LastName + ", " + contact.FirstName;
DateOfBirth = contact.DateOfBirth
};
foreach(var contact in result)
{
Console.WriteLine("{0} born on {1}", contact.Name, contact.DateOfBirth);
}
這里返回的仍是IEnumberable意荤,但其類型是什么呢?如果查看程序中的select子句只锻,就會(huì)發(fā)現(xiàn)它返回了一種新類型,其中包含字段contact.FirstName和contact.LastName的值紫谷。這實(shí)際上是一個(gè)匿名類型(anonymous type)齐饮,它包含屬性Name和DateOfBirth捐寥。這種類型之所以是匿名的,是因?yàn)樗鼪]有名稱祖驱。無需顯式地聲明與返回值對(duì)應(yīng)的新類型握恳,編譯器會(huì)自動(dòng)生成。
ps:匿名類型
以這種方式創(chuàng)建匿名類型是LINQ工作方式的核心捺僻,如果沒有var提供的引用類型乡洼,這根本不可能。
1.2 篩選數(shù)據(jù)
選擇數(shù)據(jù)很重要匕坯,但以這種方式選擇數(shù)據(jù)時(shí)束昵,無法指定要返回哪些數(shù)據(jù)。SQL 提供了where子句葛峻,同樣锹雏,LINQ也提供了where子句,它返回一個(gè)可枚舉的集合术奖,其中包含符合指定條件的元素礁遵。如下代碼再上一個(gè)示例的查詢中添加了一個(gè)where子句,將結(jié)果限定為StateProvince字段為FL的聯(lián)系人采记。
var result = from contact in contacts
whrere contact.StateProvince == "FL"
select new {customer.FirstName, customer.LastName};
foreach(var name in result)
{
Console.WriteLine(name.FirstName + " " + name.LastName);
}
首先執(zhí)行where子句佣耐,再對(duì)得到的可枚舉集合執(zhí)行select子句,其結(jié)果為一個(gè)包含屬性FirstName和LastName的匿名類型唧龄。
1.3 對(duì)數(shù)據(jù)進(jìn)行分組和排序
為支持更復(fù)雜的情形兼砖,如對(duì)返回的數(shù)據(jù)進(jìn)行排序或分組,LINQ 提供了 orderby 子句和group 子句选侨∫从悖可將數(shù)據(jù)按升序(從最小到最大)或降序(從最大到最小)排列援制,由于升序是默認(rèn)設(shè)置戏挡,因此不需要指定升序。如下程序?qū)⒔Y(jié)果按字段LastName排序晨仑。
var result = from contact in contacts
oderby contact.LastName
select contact.FirstName;
foreach(var name in result)
{
Console.WriteLine(name);
}
可根據(jù)多個(gè)字段進(jìn)行排序褐墅,并混合使用升序和降序,這將創(chuàng)建出非常復(fù)雜的 orderby 語句洪己,如下代碼所示:
var result = from contact in contacts
oderby
contact.LastName ascending,
contact.FirstName descending
select customer.FirstName;
foreach(var name in result)
{
Console.WriteLine(name);
}
將數(shù)據(jù)分組的方法與此類似妥凳,但將使用group子句替換select子句。對(duì)數(shù)據(jù)分組時(shí)的不同之處在于答捕,返回的結(jié)果為由 IGrouping<TKey, TElement>對(duì)象組成的 IEnumerable逝钥,可將其視為由列表組成的列表。這要求使用兩條嵌套的foreach語句來訪問結(jié)果拱镐。
如下代碼是一個(gè)使用group子句的LINQ查詢
var result = from contact in contacts
group contact by contact.LastName[0];
foreach(var group in result){
Console.WriteLine("Last names starting width {0}", group.key);
foreach(var name in result)
{
Console.WriteLine(name);
}
Console.WriteLine();
}
如果需要引用分組操作的結(jié)果艘款,就可創(chuàng)建一個(gè)標(biāo)識(shí)符持际,使用關(guān)鍵字into將查詢結(jié)果存儲(chǔ)到該標(biāo)識(shí)符中,并對(duì)其做進(jìn)一步查詢哗咆。這種組合方式稱為查詢延續(xù)(continuation)蜘欲。
如下代碼演示了一個(gè)使用group和into的LINQ查詢:
var result = from contact in contacts
group contact by contact.LastName[0] into namesGroup
where namesGroup.Count() > 2
select namesGroup;
foreach(var group in result)
{
Console.WriteLine("Last names starting width {0}", group.key);
foreach(var name in result)
{
Console.WriteLine(name);
}
Console.WriteLine();
}
1.4 聯(lián)接數(shù)據(jù)
LINQ 還能夠合并多個(gè)數(shù)據(jù)源,這是通過利用一個(gè)或多個(gè)都有的字段將它們聯(lián)接起來實(shí)現(xiàn)的晌柬。查詢多個(gè)沒有直接關(guān)系的數(shù)據(jù)源時(shí)姥份,聯(lián)接數(shù)據(jù)很重要。SQL支持使用很多運(yùn)算符進(jìn)行聯(lián)接年碘,但LINQ聯(lián)接基于相等性澈歉。
前面的示例只使用了Contact類,要執(zhí)行聯(lián)接操作盛泡,至少需要兩個(gè)類闷祥。程序清單12.9再次列出了Contact類,還列出了新增的JournalEntry類傲诵。繼續(xù)假設(shè)通過調(diào)用GetContacts填充了contacts列表凯砍,并通過調(diào)用GetJournalEntries填充了journal列表。
如下代碼演示了Contact和JournalEntry類
class Contact
{
public int Id { get; set; }
public string Company { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
}
class JournalEntry
{
public int Id { get; set; }
public int ContactId { get; set; }
public string Description { get; set; }
public string EntryType { get; set; }
public DateTime Date { get; set; }
}
IEnumberable<Contact> contacts = GetContacts();
IEnumberable<JournalEntry> journal = GetJournalEntries();
在LINQ中拴竹,最簡(jiǎn)單的聯(lián)接查詢與SQL內(nèi)聯(lián)接等效悟衩,這種查詢使用join子句。SQL聯(lián)接可使用很多不同的運(yùn)算符栓拜,而LINQ聯(lián)接只能使用相等運(yùn)算符座泳,稱之為相等聯(lián)接(equijoin)。
如下示例的查詢使用Contact.ID和JournalEntry.ContactId作為聯(lián)接鍵幕与,將一個(gè)由Contact對(duì)象組成的列表和一個(gè)由JournalEntry對(duì)象組成的列表聯(lián)接起來:
var result =
from contact in contacts
join journalEntry in journal
on contact.Id equals journalEntry.ContactId
select new
{
contact.FirstName,
contact.LastName,
journalEntry.Date,
journalEntry.EntryType,
journalEntry.Description
};
如上所示的 join子句創(chuàng)建了一個(gè)名為 journalEntry的范圍變量(range ariable)挑势,其類型為JournalEntry;然后使用equals運(yùn)算符將兩個(gè)數(shù)據(jù)源聯(lián)接起來啦鸣。
LINQ還支持分組聯(lián)接概念潮饱,而SQL沒有與之對(duì)應(yīng)的查詢。分組聯(lián)接使用關(guān)鍵字into诫给,其結(jié)果為層次結(jié)構(gòu)香拉。就像使用group子句時(shí)那樣,需要使用嵌套foreach語句來訪問結(jié)果中狂。
ps:順序很重要
使用LINQ聯(lián)接時(shí)凫碌,順序很重要。被聯(lián)接的數(shù)據(jù)源必須位于equals運(yùn)算符左邊胃榕,而聯(lián)接數(shù)據(jù)源必須位于右邊盛险。在這個(gè)示例中,contacts是被聯(lián)接的數(shù)據(jù)源,而journal是聯(lián)接數(shù)據(jù)源枉层。
所幸的是泉褐,順序不正確時(shí),編譯器能夠捕獲并生成編譯錯(cuò)誤鸟蜡。如果交換join子句中的參數(shù),將出現(xiàn)下面的編譯錯(cuò)誤:
名稱“Journalentry”不在“equals”左側(cè)的范圍內(nèi)挺邀。請(qǐng)考慮交換“equals”兩側(cè)的表達(dá)式揉忘。
需要注意的另一個(gè)重點(diǎn)是,join子句使用運(yùn)算符equals端铛,它與相等運(yùn)算符(==)不完全相同泣矛。
如下代碼的查詢聯(lián)接contacts和journal,并將結(jié)果按聯(lián)系人姓名分組禾蚕。在返回的結(jié)果集中您朽,每個(gè)元素都包含一個(gè)由 JournalEntry 組成的可枚舉集合,該集合由返回的匿名類型的JournalEntries屬性表示换淆。
var result =
from contact in contacts
join journalEntry in journal
on contact.Id equals journalEntry.ContactId
into journalGroups
select new
{
Name = contact.LastName + "," +contact.FirstName,
JournalEntries = journalGroups
};
1.5 數(shù)據(jù)平坦化
雖然選擇和聯(lián)接數(shù)據(jù)時(shí)哗总,返回的數(shù)據(jù)是合適的,但層次型數(shù)據(jù)使用起來比較繁瑣倍试。LINQ能夠創(chuàng)建返回平坦化數(shù)據(jù)的查詢讯屈,就像查詢SQL數(shù)據(jù)源一樣。
假設(shè)對(duì)Contact和JournalEntry類進(jìn)行了修改:在Contact類中添加了一個(gè)Journal字段县习,其類型為L(zhǎng)ist<JournalEntries>涮母,并刪除了JournalEntry類的屬性ContactId,如下所示
class Contact
{
public int Id { get; set; }
public string Company { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string StateProvince { get; set; }
public List<JournalEntries> Journal;
}
class JournalEntry
{
public int Id { get; set; }
public string Description { get; set; }
public string EntryType { get; set; }
public DateTime Date { get; set; }
}
IEnumerable<Contact> contacts = GetContacts();
在這種情況下躁愿,可使用查詢檢索特定聯(lián)系人的JournalEntry列表叛本,如下所示:
var result =
from contact in contacts
where contact.id == 1
select contact.Journal;
foreach(var item in result)
{
foreach(var journalEntry in item)
{
Console.WriteLine(journalEntry);
}
}
雖然這樣可行,也返回了所需的結(jié)果彤钟,但是仍需使用嵌套 foreach 語句來訪問結(jié)果来候。所幸的是,LINQ 支持從多個(gè)數(shù)據(jù)源選擇數(shù)據(jù)样勃,從而提供了一種返回平坦化數(shù)據(jù)的查詢語法吠勘。如下程序演示了這種查詢語法,它使用多個(gè)from子句峡眶,使得訪問數(shù)據(jù)時(shí)只需一條foreach語句剧防。
var result =
from contact in contacts
from journalEntry in contact.Journal
where contact.id == 1
select contact.Journal;
foreach(var item in result)
{
Console.WriteLine(journalEntry);
}
二、標(biāo)準(zhǔn)查詢運(yùn)算符方法
前面介紹的所有查詢都使用聲明性查詢語法辫樱,但也可使用標(biāo)準(zhǔn)查詢運(yùn)算符方法來編寫這些查詢峭拘。標(biāo)準(zhǔn)查詢運(yùn)算符方法實(shí)際上是命名空間System.Linq中定義的Enumerable類的擴(kuò)展方法。對(duì)于使用聲明性語法的查詢表達(dá)式,編譯器將其轉(zhuǎn)換為等價(jià)的查詢運(yùn)算符方法調(diào)用鸡挠。
使用using語句包含命名空間System.Linq后辉饱,對(duì)于任何實(shí)現(xiàn)了接口IEnumberable<T>的類,智能感知列表都可包含標(biāo)準(zhǔn)查詢運(yùn)算符方法拣展。
雖然聲明性查詢語法幾乎支持所有的查詢操作彭沼,但是也有一些操作(如Count和Max)沒有對(duì)應(yīng)的查詢語法,必須使用方法調(diào)用來表示备埃。由于每個(gè)方法調(diào)用都返回IEnumerable姓惑,因此通過串接方法調(diào)用,可編寫出復(fù)雜的查詢按脚。編譯聲明性查詢表達(dá)式時(shí)于毙,編譯器就是這樣做的。
ps:使用聲明性語法還是方法語法
使用聲明性語法還是方法語法因人而異辅搬,這取決于個(gè)人認(rèn)為哪種語法更容易理解唯沮。無論使用哪種語法,執(zhí)行查詢得到的結(jié)果都相同堪遂。
如下示例演示了使用方法語法的LINQ查詢
var result = contacts.
Where(contact => contact.StateProvince == "FL").
Select(contact => new { contact.FirstName, contact.LastName });
foreach(var name in result)
{
Console.WriteLine(name.FirstName + " " + name.LastName);
}
三介蛉、Lambda
上述示例中,傳遞給方法Where和Select的參數(shù)看起來與以前使用過的參數(shù)不同蚤氏。這些參數(shù)實(shí)際上包含的是代碼甘耿,而不是數(shù)據(jù)。之前介紹過委托和匿名方法竿滨,委托能夠?qū)⒁粋€(gè)方法作為參數(shù)傳遞給另一個(gè)方法佳恬,而匿名方法能夠編寫未命名的內(nèi)聯(lián)語句塊,這些語句塊將在調(diào)用委托時(shí)執(zhí)行于游。
Lambda 結(jié)合使用了這兩個(gè)概念毁葱,它是可包含表達(dá)式和語句的匿名函數(shù)。通過使用Lambda贰剥,可以更方便倾剿、更簡(jiǎn)潔的方式編寫這樣的代碼,即正常情況下需要使用匿名方法或泛型委托進(jìn)行編寫蚌成。
ps:Lambda和委托
由于Lambda是編寫委托的更簡(jiǎn)潔方式前痘,因此可在通常需要使用委托的任何地方使用它們。所以担忧,Lambda的形參類型必須與相應(yīng)的委托類型完全相同芹缔,返回類型也必須隱式地轉(zhuǎn)換為委托的返回類型。
雖然 Lambda 沒有類型瓶盛,但是它們可隱式地轉(zhuǎn)換為任何兼容的委托類型最欠。正是這種隱式轉(zhuǎn)換讓您無需顯式賦值就能夠傳遞它們示罗。
在C#中,Lambda使用Lambda運(yùn)算符(=>)芝硬。在方法調(diào)用中蚜点,該運(yùn)算符左邊指定了形參列表,而該運(yùn)算符右邊為方法體拌阴。匿名方法的所有限制也適用于Lambda绍绘。
在上述示例中,實(shí)參 contact => contact.StateProvince == "FL"的意思為迟赃,這是一個(gè)以 contact為參數(shù)的函數(shù)脯倒,其返回值為表達(dá)式 contact.StateProvince == "FL"的結(jié)果。
ps:捕獲的變量和定義的變量
Lambda還能捕獲變量捺氢,這可以是Lambda所屬方法的局部變量或參數(shù)。這使得可在Lambda體內(nèi)通過名稱訪問捕獲的變量剪撬。如果捕獲是局部變量摄乒,那么必須賦值后才能在Lambda中使用它。ref或out參數(shù)無法捕獲残黑。
然而馍佑,需要注意的是,對(duì)于Lambda捕獲的變量梨水,在引用它的委托超出作用域前拭荤,垃圾收集器將不會(huì)收集它們。
Lambda中聲明的變量在Lambda所屬方法內(nèi)不可見疫诽,輸入?yún)?shù)也如此舅世,因此可在多個(gè)Lambda中使用同一個(gè)標(biāo)識(shí)符。
表達(dá)式Lambda
在Lambda中奇徒,如果運(yùn)算符右邊為表達(dá)式雏亚,該Lambda就為表達(dá)式Lambda,它返回該表達(dá)式的結(jié)果摩钙。表達(dá)式Lambda的基本格式如下:
(input parameters) => expressions
如果只有一個(gè)輸入?yún)?shù)罢低,那么括號(hào)是可選擇的;否則(包括沒有參數(shù)時(shí))胖笛,括號(hào)將是必不可少的网持。
就像泛型方法可推斷其類型參數(shù)的類型一樣,Lambda 也能推斷其輸入?yún)?shù)的類型长踊。如果編譯器無法推斷出類型功舀,您就可以顯式地指定類型。
如果將表達(dá)式 Lambda 的表達(dá)式部分視為方法體之斯,那么表達(dá)式 Lambda 包含一條隱式的return語句日杈,它返回表達(dá)式的結(jié)果遣铝。
ps:包含方法調(diào)用的表達(dá)式Lambda
大部分示例都在右邊使用了方法,但是如果創(chuàng)建的Lambda將用于其他域莉擒,如SQL Server酿炸,就不應(yīng)使用方法調(diào)用,因?yàn)樗鼈冊(cè)?NET Framework公共語言運(yùn)行時(shí)外面沒有意義涨冀。
語句Lambda
在 Lambda 的右邊稠氮,可使用一條或多條用大括號(hào)括起的語句,這種 Lambda 稱為語句Lambda味滞。語句Lambda的基本形式如下:
(input parameters) => { statement; }
與表達(dá)式 Lambda 一樣疫衩,如果只有一個(gè)輸入?yún)?shù),那么括號(hào)是可選的翅帜;否則姻檀,括號(hào)就必不可少。語句Lambda也遵循同樣的類型推斷規(guī)則涝滴。
雖然表達(dá)式Lambda包含一條隱式的return語句绣版,但是語句Lambda沒有,您必須在語句Lambda中顯式地指定return語句歼疮。return語句只導(dǎo)致從Lambda表示的隱式方法返回杂抽,而不會(huì)導(dǎo)致從Lambda所屬的方法返回。
語句Lambda不能包含這樣的goto韩脏、break和continue語句缩麸,即其跳轉(zhuǎn)目標(biāo)在Lambda外。同樣赡矢,作用域規(guī)則禁止從嵌套Lambda分支到外部Lambda杭朱。
預(yù)定義的委托
雖然 Lambda 是 LINQ 的有機(jī)組成部分,但是可將其用于任何可使用委托的地方济竹。因此痕檬,.NET Framework提供了很多預(yù)定義的委托,可將其作為方法參數(shù)進(jìn)行傳遞送浊,而無需首先聲明顯式的委托類型梦谜。
由于返回Boolean值的委托很常見,因此.NET Framework定義了一個(gè)Predicate<in T>委托袭景,Array和List<T>類的很多方法都使用它唁桩。
Predicate<T>定義了一個(gè)總是返回Boolean值的委托,而Func系列委托封裝了有指定返回值耸棒,且接受0~16個(gè)輸入?yún)?shù)的方法荒澡。
Predicate<T>和 Func 系列委托都有返回值,但是 Action 系列委托表示返回類型為 void的方法与殃。就像Func系列委托一樣单山,Action系列委托也接受0~16個(gè)輸入?yún)?shù)碍现。
四、延遲執(zhí)行
不同于眾多傳統(tǒng)的數(shù)據(jù)查詢技術(shù)米奸,LINQ 查詢要等到實(shí)際迭代其結(jié)果時(shí)才執(zhí)行昼接,稱之為延遲執(zhí)行(lazy evaluation)。其優(yōu)點(diǎn)之一是悴晰,在指定查詢和檢索查詢指定的數(shù)據(jù)之間慢睡,可修改原始集合中的數(shù)據(jù)。這意味著您獲得的數(shù)據(jù)總是最新的铡溪。
雖然LINQ首選延遲執(zhí)行漂辐,但是使用了任何聚合函數(shù)的查詢都必須先迭代所有元素。這些函數(shù)(如Count棕硫、Max髓涯、Average和First)都返回一個(gè)值,并且無需使用顯式foreach語句就能執(zhí)行哈扮。
ps:延遲執(zhí)行和串接查詢
延遲執(zhí)行的另一個(gè)優(yōu)點(diǎn)是复凳,讓您能夠串接查詢,從而提供編碼效率灶泵。由于查詢對(duì)象表示的是查詢,而不是查詢的結(jié)果对途,因此可輕松地串接或重用它們赦邻,而不會(huì)導(dǎo)致開銷高昂的數(shù)據(jù)取回操作。
也可強(qiáng)制查詢立刻執(zhí)行实檀,這有時(shí)稱為貪婪執(zhí)行(greedy evaluation)惶洲。為此,可在查詢表達(dá)式后面膳犹,緊接著放置一條foreach語句恬吕,也可調(diào)用方法ToList 或ToArray。方法ToList 和ToArray還可用于將數(shù)據(jù)緩存到一個(gè)集合對(duì)象中须床。