本篇不是對標題所述之概念的入門文章,重點在闡述它們的異同點和應用場景。各位看官,這里就不啰嗦了,直接上代碼。
首先定義一個泛型委托類型,如下:
public delegate T Function<T>(T a, T b);
實現泛型委托的主體代碼,並調用:
public static string Add(string a, string b)
{
return string.Format("{0} #### {1}",a,b);
}
//實名委托方式 Function<string> func = new Function<string>(Add); Console.WriteLine( func("hello", "world") ); //匿名委托方式 Function<string> func1 = new Function<string>(delegate(string a, string b) { return string.Format("{0} @@@@ {1}",a,b); }); Console.WriteLine(func1("hello", "world")); //Lambda表達式方式 Function<string> func2 = (a, b) => string.Format("{0} **** {1}", a, b); Console.WriteLine(func2("hello", "world")); Expression<Function<string>> func2_0; //func2_0 = func; //不支持將委托直接賦值給表達式樹 //func2_0 = func1; //不支持將委托直接賦值給表達式樹 //func2_0 = func2; //不支持將委托直接賦值給表達式樹 //(a, b) => string.Format("{0} **** {1}", a, b)語句塊的類型是lambda expression,即我們常說的lambda表達式 //所以,func2_0 = (a, b) => string.Format("{0} **** {1}", a, b)的直接賦值是沒有問題的。 func2_0 = (a, b) => string.Format("{0} **** {1}", a, b); Console.WriteLine(func2_0.Compile()("hello", "world"));
以上代碼展示了委托類型Function<T>主體定義的四種方式,分別是實名委托、匿名委托、Lambda表達式、expression表達式樹。
從Function<T>委托主體的代碼定義來看是越來越簡單和友好,這些變化很大部分應歸功於C#的語法糖。
總結:不管委托主體在編寫的形式上怎么簡化,但依然改變不了它委托類型的本質,當委托代碼塊被調用時會即時執行。
隨着C#的發展,后來加入了expression這個東東,簡稱表達式樹,我想用過ling to sql、linq to entity、linq to xml等等的你是不會陌生的。
expression是一種數據結構,我們可以將平常編寫的C#語句塊(或者叫表達式)的各部分進行分解並存入這個樹結構當中,保存在expression樹結構中的語句塊是不能直接執行的。
當我們需要將expression結構中的數據抽取並還原時就需要調用expression.Compile()方法,這里我稱之為編譯。編譯后得到的結果就是我們之前存入的語句塊,這是數據結構還原成語句塊的過程(這是一個比喻)。
當然將數據還原成語句塊時依據解析引擎的不同會產生不同的輸出結果,如果引擎是linq to sql那么解析后輸出的就是可供數據庫執行的sql,如果引擎是linq to xml則解析后輸出的是Xpath之類的表達式(沒親自驗證)
下面就請你和我一起來體驗一下expression表達式數據的存儲和編譯輸出吧!!!!仍以上面的場景為例子。
//expression表達式樹主體構造開始 ParameterExpression paramA = Expression.Parameter(typeof(object), "a"); //聲明Lambda表達式中的參數表達式a ParameterExpression paramB = Expression.Parameter(typeof(object), "b"); //聲明Lambda表達式中的參數表達式b ConstantExpression constantExp = Expression.Constant("{0} !!!!! {1}",typeof(string));//聲明文本塊常量表達式 MethodCallExpression bodyExp = Expression.Call(typeof(string).GetMethod("Format", new Type[] { typeof(string), typeof(object), typeof(object) }) , new Expression[] { constantExp, paramA, paramB }); //聲明String.Format()方法調用表達式 //expression表達式樹主體構造結束 //1.構造類型為LambdaExpression的lambda表達式樹,編譯后得到委托的基元類型(弱類型)。 LambdaExpression func3 = Expression.Lambda(bodyExp, paramA, paramB);//將以上各個表達式部分組合為Lambda表達式 Delegate dg = func3.Compile();//編譯表達式樹得到委托 Console.WriteLine(dg.DynamicInvoke("hello", "world"));//調用委托並將結果輸出到控制台 //Console.WriteLine(func3.Compile().DynamicInvoke("hello", "world")); //上面兩步可以簡化為這句代碼 //2.構造類型為Expression<Function<string>>的泛型lambda表達式樹,編譯后得到委托可直接調用。 Expression<Function<string>> func4 = Expression.Lambda<Function<string>>(bodyExp, paramA, paramB); Console.WriteLine(func4.Compile()("xxxx", "yyyy")); //3.構造類型為Expression<Func<string, string, string>>的泛型lambda表達式樹,編譯后得到委托可直接調用。 //與上面的區別是這里用系統定義的Func<in T1, in T2, out TResult>泛型委托代替了自定義的Function<T>委托。 Expression<Func<string, string, string>> func5 = Expression.Lambda<Func<string, string, string>>(bodyExp, paramA, paramB); Console.WriteLine(func5.Compile()("yyyy", "zzzz")); //以上總結了expression表達式的創建和調用的不同方式,以下是幾個有關expression的擴展例子 //4.動態構造string.Concat("hello", "world")語句塊 var concatMethod = typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string) }); var addExpr = Expression.Add(Expression.Constant("hello "), Expression.Constant("world"), concatMethod); Expression<Func<string>> e = Expression.Lambda<Func<string>>(addExpr); Console.WriteLine(e.Compile()()); //5.動態構造Math.Sin(100)語句塊 ParameterExpression expA = Expression.Parameter(typeof(double), "a"); //參數a MethodCallExpression expCall = Expression.Call( typeof(Math).GetMethod("Sin",new Type[]{typeof(double)}),expA); LambdaExpression exp = Expression.Lambda(expCall, expA); // a => Math.Sin(a) Console.WriteLine( exp.Compile().DynamicInvoke(100) );
//6.動態構造Console.WriteLine("aaa")語句塊 ConstantExpression _constExp = Expression.Constant("aaa", typeof(string));//一個常量 MethodCallExpression _methodCallexp = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }), _constExp); Expression<Action> consoleLambdaExp = Expression.Lambda<Action>(_methodCallexp); consoleLambdaExp.Compile()();
關於xx.Where(Func<T,bool> predicate)和xx.Where(Expression<Func<T, bool>> predicate)的一點看法
//一般用在對內存對象的篩選場景下,語句被調用后即時執行並對數據進行篩選 public static IEnumerable<string> Where(Func<string,bool> predicate) { List<string> lst = new List<string>(); lst.Add("aaa"); lst.Add("bbb"); lst.Add("ccc"); IEnumerable<string> rs = lst.Where(predicate); return rs; } //基於表達式樹級別的條件篩選(比如linq to entity),表達式樹只是sql語句邏輯關系的容器,最終的sql語句在表達式樹被編譯調用后才得到。 public static IQueryable<string> Where(Expression<Func<string, bool>> predicate) { List<string> lst = new List<string>(); lst.Add("aaa"); lst.Add("bbb"); lst.Add("ccc"); IQueryable<string> rs = lst.AsQueryable().Where(predicate); return rs; }