談表示式樹的快取(1) 引言

2021-09-25 20:04:30 字數 2264 閱讀 6182

表示式樹(expression tree)是.net 3.5中引入的一種表達方式。表示式樹的運用十分廣泛,可以直觀地表現出各種「資料」,甚至「邏輯」和「行為」。再者,表示式樹是強型別的,因此合理地使用這個新特性可以讓**編寫變得優雅,方便。乙個最簡單而常見的例子便是,某些朋友目前就已經喜歡使用表示式樹來代替傳統的by***方法,尤其是在訪問一些直接支援表示式樹的資料來源時(例如ienumerable或linq to sql)。如下:

public 

user getuser(expression

> predicate)

而不必寫成:

public 

user getuserbyid(int id)

public

user getuserbyname(string name)

於是在呼叫時便可以:

var user1 = getuser(u => u.userid == 1);

var user2 = getuser(u => u.name == "jeffz");

姑且不論這種設計方式是否合適(因為即使這個做法不合理,也不能代表所有的用法),我們先達成乙個共識,那就是「表示式樹很有用」——於是我們的接下去話題看上去才會比較有價值:p。那麼就上面乙個問題來說,在使用了表示式樹的情況下,如何在方法中進行快取?在byid或byname的情況下,我們可以輕易地構造一些字串作為快取的鍵,例如「getuserbyid_100」或「getuserbyname_jeffz」。但是現在呢?每次在呼叫時就會生成乙個不同的expression物件,就算大家「表現一致」,也無法被「識別為」同樣的物件,而直接用作快取的鍵,因此處理起來並不是那麼直接了當的。

您可能會說,那麼就在解析表示式樹的時候,識別出它是byid還是byname,然後再拼接出之前的字串。當然,您如果真的是要解決上面這個例子的問題,那麼的確可以用這種方法。但是老趙現在希望可以找到一種較為通用的,能夠根據表示式樹進行快取的解決方案——事實上老趙本來就是在設計乙個通用的功能時才引發了這個需求,而這個功能也打算在詳細談完快取問題後與大家共享。

這個快取問題看上去簡單,但是實際上在效能和功能進行權衡之後會有多種策略可以選擇。老趙會在這裡談論5種快取策略,它們各有千秋,有的方式資源很省,效能很好;而有的方式從效能上比較落後,資源占用也相對較高,但是在某些場景下它似乎還是唯一的解決方案。因此,至少我覺得討論一下這個問題也是非常有意思的事情,而且從一定程度上說,這些思考能夠在一定程度上體現出演算法設計與資料結構的美妙之處(儘管相對來說它們其實非常簡單)。

在這一系列文章中,老趙希望可以重現自己在思考這個問題的時候所形成的完整思考路徑。相比最終解決方案,這可能才是更有價值的東西。文章有時也會將朋友們「引入歧途」,其目的也是為了讓弟兄們一起經歷一下老趙走過的彎路。到了最後,您可以會說「這死胖子真笨,怎麼早沒想到」(呵呵,大家莫怪)。此外,這5種快取策略也並非是思考的全部,事實上老趙相信還會有更好的解決方案(至少理論上是這樣的),而由於種種原因並沒有在這裡實現出來。因此,老趙也希望大家在看了文章之後可以一起思考,並談出您的看法。:)

不過,表示式樹的「構造」很簡單,我們可以使用lambda表示式輕鬆地生成一顆表示式樹,但是在具體操作時就較為困難了。因此在理解這一系列文章之前,可能您還需要作一些準備,也就是一些基礎的,操作表示式樹的方式。在操作表示式樹時,必不可少的東西便是乙個expressionvisitor類,您可以在msdn中找到其實現及相關示例,幾乎任何操作表示式樹的類都會繼承於它。總體來說,expressionvisitor類提供了功能可以概括為:

visit方法接受乙個expression型別的引數,並根據expression的具體型別委託給特定的visit***方法進行訪問,並將結果返回。

各visit***方法會將得到的具體expression型別的引數(如unaryexpression)的各部分,即各個「子expression」交給visit方法進行訪問,並得到其返回值。

各visit***方法如果發現每個visit方法的呼叫都返回了原來的「子expression」,那麼則直接返回自身得到的引數,否則就構造乙個新的expression物件並返回。

以上是expressionvisitor類的功能描述,希望朋友們可以自行閱讀一下它的**。最好還可以自行實踐一番——至少可以閱讀一下msdn中的示例。

最後,我們將實現幾個類,它們都實現同乙個介面iexpressioncache,如下:

public inte***ce 

iexpressioncache

where t : class

介面中只有乙個get方法,如果沒有對應當前key的value,那麼則會通過creator委託建立乙個新的value並返回。

談表示式樹的快取(1) 引言

表示式樹 expression tree 是.net 3.5中引入的一種表達方式。表示式樹的運用十分廣泛,可以直觀地表現出各種 資料 甚至 邏輯 和 行為 再者,表示式樹是強型別的,因此合理地使用這個新特性可以讓 編寫變得優雅,方便。乙個最簡單而常見的例子便是,某些朋友目前就已經喜歡使用表示式樹來代...

談表示式樹的快取(1) 引言

表示式樹 expression tree 是.net 3.5中引入的一種表達方式。表示式樹的運用十分廣泛,可以直觀地表現出各種 資料 甚至 邏輯 和 行為 再者,表示式樹是強型別的,因此合理地使用這個新特性可以讓 編寫變得優雅,方便。乙個最簡單而常見的例子便是,某些朋友目前就已經喜歡使用表示式樹來代...

談表示式樹的快取(5) 引入雜湊值 1

到目前為止,我們已經實現了三種快取方式 首先我們設法構建唯一字串 但是由於它的代價較高,於是我們使用了字首樹 進行儲存 又由於字首樹在實際操作中所花的時間和空間都有不令人滿意之處,我們又引入了二叉搜尋樹 那麼二叉搜尋樹又有什麼缺點呢?其實前文已經談到過了,那就是從理論上來說,它的時間複雜度相對前兩個...