為了進(jìn)一步加深對(duì)Lambda表達(dá)式的理解,我們需要掌握一個(gè)新的知識(shí),Lambda表達(dá)式樹(shù),可能聽(tīng)名字看起來(lái)很高深和難以理解,但實(shí)際上理解起來(lái)并沒(méi)有想象中那么難,這篇文章我想分以下幾點(diǎn)進(jìn)行總結(jié)。
1,表達(dá)式樹(shù)的語(yǔ)法
2,將代碼轉(zhuǎn)換到數(shù)據(jù)
3,探索表達(dá)式樹(shù)
4,將數(shù)據(jù)轉(zhuǎn)換到代碼
5,IQueryable<T>和表達(dá)式樹(shù)
6,為什么要將LINQ to SQL查詢表達(dá)式轉(zhuǎn)換成表達(dá)式樹(shù)?
7,IQueryable<T>和IEnumerable<T>
8,總結(jié)
//利用Lambda表達(dá)式定義一個(gè)Func委托Func<int, int, int> function = (a, b) => a + b;
變量function指向兩個(gè)數(shù)字相加的原生可執(zhí)行的代碼,上面這個(gè)Lambda表達(dá)式等價(jià)于下面這個(gè)方法。
public int function(int a, int b){ return a + b;}這個(gè)Lambda表達(dá)式和這個(gè)方法都可以這樣調(diào)用。
int c = function(1,2); //結(jié)果為:3
表達(dá)式樹(shù)不是一段可執(zhí)行代碼,而是一種數(shù)據(jù)結(jié)構(gòu)。那么怎么將Lambda表達(dá)式轉(zhuǎn)換成表達(dá)式樹(shù)呢?
我們可以使用命名空間System.Linq.ExPRessions下的Expression類(lèi)來(lái)實(shí)現(xiàn)這個(gè)需求。
例如我們先創(chuàng)建一個(gè)表達(dá)式樹(shù),如下代碼。
//創(chuàng)建表達(dá)式樹(shù)Expression<Func<int, int, int>> expression = (a, b) => a + b;
這樣,我們就創(chuàng)建一個(gè)類(lèi)型為Expression<T>的表達(dá)式樹(shù),標(biāo)識(shí)expression不是可執(zhí)行代碼;它是一個(gè)名叫表達(dá)式樹(shù)的數(shù)據(jù)結(jié)構(gòu)。我們可以使用工具ExpressionTreeVisualizer來(lái)瀏覽表達(dá)式樹(shù),如下圖。

Expression<TDelegate>類(lèi)有四個(gè)屬性:
那么怎么查看表達(dá)式樹(shù)中的參數(shù)名稱呢?從上圖中我們可以看出,參數(shù)是一個(gè)ReadOnlyCollection集合,所以我們可以通過(guò)索引來(lái)訪問(wèn)。如下代碼。
//訪問(wèn)表達(dá)式樹(shù)的參數(shù)Console.WriteLine("參數(shù)1:{0},參數(shù)2:{1}",expression.Parameters[0],expression.Parameters[1]);接下來(lái),怎么查看表達(dá)式樹(shù)的Body體呢?在這個(gè)例子里是(a+b)。代碼如下。
//訪問(wèn)表達(dá)式樹(shù)的BodyBinaryExpression body = expression.Body as BinaryExpression;ParameterExpression left = body.Left as ParameterExpression;ParameterExpression right = body.Right as ParameterExpression;Console.WriteLine(expression.Body);Console.WriteLine(" 表達(dá)式左邊部分: " + "{0}{4} 節(jié)點(diǎn)類(lèi)型: {1}{4} 表達(dá)式右邊部分: {2}{4} 類(lèi)型: {3}{4}", left.Name, body.NodeType, right.Name, body.Type, Environment.NewLine);輸出結(jié)果為:
通過(guò)探索表達(dá)式樹(shù),我們可以分析表達(dá)式的各個(gè)部分發(fā)現(xiàn)它的組成。你可以看見(jiàn),我們的表達(dá)式的所有元素都展示為像節(jié)點(diǎn)這樣的數(shù)據(jù)結(jié)構(gòu)。表達(dá)式樹(shù)是代碼轉(zhuǎn)換成的數(shù)據(jù)。
我們可以將代碼轉(zhuǎn)換為數(shù)據(jù),那么我們也可將數(shù)據(jù)轉(zhuǎn)換為代碼。下面的代碼說(shuō)明了如果將數(shù)據(jù)(表達(dá)式樹(shù)數(shù)據(jù)結(jié)構(gòu))轉(zhuǎn)換為代碼。
//將數(shù)據(jù)(表達(dá)式樹(shù))轉(zhuǎn)換為代碼int result = expression.Compile()(1,2);Console.WriteLine(result); //輸出結(jié)果為:3
可以發(fā)現(xiàn),程序輸出結(jié)果與Lambda表達(dá)式執(zhí)行結(jié)果一樣。
現(xiàn)在至少你有一個(gè)抽象的概念理解表達(dá)式樹(shù),現(xiàn)在是時(shí)候回來(lái)理解其在LINQ中的關(guān)鍵作用了,尤其是在LINQ to SQL中。花點(diǎn)時(shí)間考慮這個(gè)標(biāo)準(zhǔn)的LINQ to SQL查詢表達(dá)式:
var query = from c in db.Customers where c.City == "Nantes" select new { c.City, c.CompanyName };你可能知道,這里L(fēng)INQ表達(dá)式返回的變量query是IQueryable類(lèi)型。這里是IQueryable類(lèi)型的定義:
public interface IQueryable : IEnumerable { Type ElementType { get; } Expression Expression { get; } IQueryProvider Provider { get; }}你可以看見(jiàn),IQueryable包含一個(gè)類(lèi)型為Expression的屬性,Expression是Expression<T>的基類(lèi)。IQueryable的實(shí)例被設(shè)計(jì)成擁有一個(gè)相關(guān)的表達(dá)式樹(shù)。它是一個(gè)等同于查詢表達(dá)式中的可執(zhí)行代碼的數(shù)據(jù)結(jié)構(gòu)。
現(xiàn)在我們知道,表達(dá)式樹(shù)是一個(gè)用來(lái)表示可執(zhí)行代碼的數(shù)據(jù)結(jié)構(gòu)。那我們?yōu)槭裁匆獙INQ to SQL查詢表達(dá)式轉(zhuǎn)換成表達(dá)式樹(shù)呢?
一個(gè)LINQ to SQL查詢不是在C#程序里執(zhí)行的,而是被轉(zhuǎn)換成SQL語(yǔ)句,通過(guò)網(wǎng)絡(luò)發(fā)送,最后在數(shù)據(jù)庫(kù)服務(wù)器上執(zhí)行的。也就是說(shuō),下面這個(gè)LINQ查詢不是在C#程序里執(zhí)行的。
var query = from c in db.Customers where c.City == "Nantes" select new { c.City, c.CompanyName };它是被轉(zhuǎn)換成SQL語(yǔ)句后在數(shù)據(jù)庫(kù)服務(wù)器上運(yùn)行的。轉(zhuǎn)換后的SQL語(yǔ)句如下代碼。
SELECT [t0].[City], [t0].[CompanyName]FROM [dbo].[Customers] AS [t0] WHERE [t0].[City] = @p0
現(xiàn)在也許可以回答上面的問(wèn)題。可以用一句話總結(jié):表達(dá)式樹(shù)是為了更方面地將查詢表達(dá)式轉(zhuǎn)換成字符串(這里指的是SQL語(yǔ)句)并交給其它程序(這里一般指數(shù)據(jù)庫(kù)服務(wù)器)執(zhí)行。
我們知道,LINQ to Objects通常返回IEnumerable<T>,而LINQ to SQL返回的是IQueryable<T>。那為什么它們返回的類(lèi)型會(huì)不一樣呢?
我們先來(lái)看它們的定義,也許我們可以從它們的定義中找到問(wèn)題的答案。
IEnumberable<T>的定義如下:
public interface IEnumerable<T> : IEnumerable { IEnumerator<T> GetEnumerator();}IQueryable<T>的定義如下:
public interface IQueryable : IEnumerable { Type ElementType { get; } Expression Expression { get; } //表達(dá)式樹(shù) IQueryProvider Provider { get; }}可以看出,IQueryable<T>包含一個(gè)Expression表達(dá)式樹(shù)的定義而IEnumberable<T>卻沒(méi)有,這同時(shí)也揭示了一個(gè)現(xiàn)象,表達(dá)式樹(shù)通常用在LINQ to SQL查詢中,而LINQ to Objects中卻很少使用。
那為什么LINQ to Objects中很少使用表達(dá)式樹(shù)呢?是因?yàn)長(zhǎng)INQ to Objects查詢通常在.net程序中就可以完成,不需要將其轉(zhuǎn)換成字符串(或SQL語(yǔ)句)發(fā)送到其它程序中執(zhí)行。
那么針對(duì)這兩種返回類(lèi)型,我們?cè)撛趺催x擇呢?這里有兩條原則可以參考:
通過(guò)以上內(nèi)容的學(xué)習(xí),發(fā)現(xiàn)表達(dá)式樹(shù)并沒(méi)有想象中那么難以理解,關(guān)于表達(dá)式樹(shù)我想用以一幾句通俗易懂的話總結(jié)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注