C# Notizen 12 查詢表達(dá)式

應(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ì)象中须床。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末铐料,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子豺旬,更是在濱河造成了極大的恐慌钠惩,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件族阅,死亡現(xiàn)場(chǎng)離奇詭異篓跛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)坦刀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門愧沟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔬咬,“玉大人,你說我怎么就攤上這事沐寺×炙遥” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵芽丹,是天一觀的道長(zhǎng)北启。 經(jīng)常有香客問我,道長(zhǎng)拔第,這世上最難降的妖魔是什么咕村? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蚊俺,結(jié)果婚禮上懈涛,老公的妹妹穿的比我還像新娘。我一直安慰自己泳猬,他們只是感情好批钠,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著得封,像睡著了一般埋心。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上忙上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天拷呆,我揣著相機(jī)與錄音,去河邊找鬼疫粥。 笑死茬斧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梗逮。 我是一名探鬼主播项秉,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼慷彤!你這毒婦竟也來了娄蔼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤底哗,失蹤者是張志新(化名)和其女友劉穎贷屎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艘虎,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唉侄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了野建。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片属划。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恬叹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出同眯,到底是詐尸還是另有隱情绽昼,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布须蜗,位于F島的核電站硅确,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏明肮。R本人自食惡果不足惜菱农,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柿估。 院中可真熱鬧循未,春花似錦、人聲如沸秫舌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽足陨。三九已至嫂粟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間墨缘,已是汗流浹背赋元。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留飒房,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓媚值,卻偏偏與公主長(zhǎng)得像狠毯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子褥芒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容