Lambda表達式可以轉換成為代碼(委托)或者數據(表達式樹)。若將其賦值給委托,則Lambda表達式將轉換為IL代碼;如果賦值給 Expression<TDelegate>,則構造出一顆表達式樹。表達式樹本質上來說就是一顆抽象語法樹(AST),也就是一段代碼經過 解析后用樹形來表達出這段代碼的意思。解釋器將在代碼優化和代碼生成的時候使用到AST。在.NET中,表達式樹就是C#編譯器解析lambda表達式的 結果。簡單來說,轉換成表達式樹以后,我們可以通過自己的解釋器解析表達式樹來按需求實現自己的邏輯。
比如想表達加法,用中文寫就是 “二大於一” ,用數學來表達就是 "2>1",我們想表達的抽象概念就是大於,和具體的形式無關。因此表達式樹中就有表示GreaterThan的一種Type,表達的就是這么一種 大於的抽象概念。它可以由編譯器把lambda表達式 ()=> 2>1 編譯成我們所需的表達式樹,然后我們再通過解析這個表達式樹,把抽象概念翻譯成我們所需的“二大於一”這種中文的具體形式。
有這么一個笑話,說的是三國版批評與自我批評:
”關羽:我要批評張飛,平時說話聲音太大,雖然用意是關心將士溫飽,但說話的樣子很凶,不利於團結基層兵士。 張飛:我批評趙雲,身為大將,衣着太干凈、太鮮亮,看起來很驕傲。趙雲:我要批評關羽,你的赤兔馬違反了公務用馬管理辦法,屬於超豪華配備吧?關羽:X, 你TM懂不懂什么叫批評啊?會不會玩啊?“
用上面的例子來說,就是lambda表達式要表達的是“批評他人”這個意思,但是不同的人解讀“批評他人”是不同的。可以用訪問者模式來做到這一點。
C#中不是每個類型都能相加的,編譯器會報錯。但是我們表達的意思是兩種類型相加這一通用的概念,因此這時候就可以用表達式樹來表達這一種概念,來“繞過”編譯器限制。這是合理的。
我 們可以自定義對於表達式中相加Expression.Add的理解,也可以由C#編譯器按照它的理解來幫我們編譯成可執行的匿名函數。C#編譯器理解中並 不是每種類型都可以相加的,因此如果Expression.compile成Func類型函數那么在執行的時候就有可能會報Exception,比如 "The binary operator Add is not defined for the types 'System.String' and 'System.String'."
表達式樹的順序與遍歷……
編譯器可以由lambda表達式幫我們生成一顆表達式樹,我們接下來解決的就是要如何解析這個樹的問題。對於樹我們采用至頂向下的遍歷方式,借助訪問者模式去解析表達式樹。
表達式樹的生成
當編譯器看到某個lambda表達式賦值給了類型為Expression的變量的時候,就會將其編譯成對一系列工廠方法的調用,這些工廠方法將會在程序運行時動態的構造出表達式樹。
表達式樹將在程序運行時動態構造,不過一旦構造完成,則無法被再次修改。
public abstract class QueryProvider : IQueryProvider { protected QueryProvider() { } IQueryable<S> IQueryProvider.CreateQuery<S>(Expression expression) { return new Query<S>(this, expression); } IQueryable IQueryProvider.CreateQuery(Expression expression) { Type elementType = TypeSystem.GetElementType(expression.Type); try { return (IQueryable)Activator.CreateInstance(typeof(Query<>).MakeGenericType(elementType), new object[] { this, expression }); } catch (TargetInvocationException tie) { throw tie.InnerException; } } IQueryProvider.Execute<S>(Expression expression) { return (S)this.Execute(expression); } object IQueryProvider.Execute(Expression expression) { return this.Execute(expression); } public abstract string GetQueryText(Expression expression); public abstract object Execute(Expression expression); }
public class AmazonBookQueryProvider : QueryProvider { public override String GetQueryText(Expression expression) { AmazonBookQueryCriteria criteria; // Retrieve criteria criteria = new AmazonBookExpressionVisitor().ProcessExpression(expression); // Generate URL String url = AmazonHelper.BuildUrl(criteria); return url; } public override object Execute(Expression expression) { String url = GetQueryText(expression); IEnumerable<AmazonBook> results = AmazonHelper.PerformWebQuery(url); return results; } }
解析一個表達式樹的例子
