時間:午飯后
地點:蘇州公司
主題:Entity Framework涉及的一些C#語言特性,要講的語言特性,如下圖所示

1.Extension Methods
很舊的主題了,DebugLZQ以前也有相關的博文提過。用個簡單點的例子來說吧
下面的例子完成一個計算某個月剩余天數的功能,我們可以如下編碼:調用一個靜態幫助類的靜態方法。
using System; namespace ExtensionMethods { class Program { static void Main(string[] args) { DateTime dateTime = new DateTime(2013, 3, 22); int daysTillEndOfMonth = DateUtilities.DaysToEndOfMonth(dateTime); Console.WriteLine(daysTillEndOfMonth); Console.ReadKey(); } } public static class DateUtilities { public static int DaysToEndOfMonth(DateTime dateTime) { return DateTime.DaysInMonth(dateTime.Year, dateTime.Month) - dateTime.Day; } } }
這樣寫當然沒什么問題。很多時候就是這樣用的
如果我們用擴展方法,可以如下實現,注意區別在哪里!
using System; namespace ExtensionMethods { class Program { static void Main(string[] args) { DateTime dateTime = new DateTime(2013, 3, 22); int daysTillEndOfMonth = dateTime.DaysToEndOfMonth(); Console.WriteLine(daysTillEndOfMonth); Console.ReadKey(); } } public static class DateUtilities { public static int DaysToEndOfMonth(this DateTime dateTime) { return DateTime.DaysInMonth(dateTime.Year, dateTime.Month) - dateTime.Day; } } }
下面用擴展方法實現的程序明顯比上面的普通函數調用,更容易閱讀。
其實,擴展方法也就是C#的一個語法糖衣,其編譯過后也是方法的調用,其優點是更容易閱讀,因為其可以為一些固有的類如DateTime、String...擴展一些方法。
2.Query Language
下面通過上面的擴展方法,來實現數據查詢。可以實現如下:
using System; using System.Collections.Generic; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"}; IEnumerable<string> query = cities.StringThatStartWith("S"); foreach(var city in query) { Console.WriteLine(city); } Console.ReadKey(); } } //寫一個擴展方法用迭代器實現過濾 public static class FilterExtension { public static IEnumerable<string> StringThatStartWith(this IEnumerable<string> input,string start) { foreach(var s in input) { if (s.StartsWith(start)) yield return s; } } } }
上面的代碼運行當然也木有什么問題,但是實現方法有點繁瑣。
可以初步簡化如下:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"}; IEnumerable<string> query = cities.StringThatStartWith("S"); foreach(var city in query) { Console.WriteLine(city); } Console.ReadKey(); } } // public static class FilterExtension { public static IEnumerable<string> StringThatStartWith(this IEnumerable<string> input,string start) { return input.Where(s => s.StartsWith(start)); } } }
進一步簡化:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"}; IEnumerable<string> query = cities.Where(s => s.StartsWith("S")); foreach(var city in query) { Console.WriteLine(city); } Console.ReadKey(); } } }
不喜歡這種寫法?可以用LINQ來寫,如下:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] {"Suzhou","Wuxi","Changzhou","Zhenjiang","Nanjing","Shanghai"}; //IEnumerable<string> query = cities.Where(s => s.StartsWith("S")); IEnumerable<string> query = from city in cities where city.StartsWith("S") select city; foreach(var city in query) { Console.WriteLine(city); } Console.ReadKey(); } } }
3.Oh,Holy Shit!
Where從哪里冒出來的!盜用了MS的東西!這里:
IEnumerable<string> query = cities.Where(s => s.StartsWith("S"));//這個“Where”,MS是怎么做到的?自己定義一個Filter怎么樣?
讓我們回到那個擴展方法,從頭來實現 ,如下:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" }; IEnumerable<string> query = cities.Filter(StringThatStartWith); foreach (var city in query) { Console.WriteLine(city); } Console.ReadKey(); } static bool StringThatStartWith(string item,string startWord) { return item.StartsWith(startWord); } } //委托作為擴展方法的參數 public static class FilterExtension { public delegate bool FilterDelegate(string item,string startWord); public static IEnumerable<string> Filter(this IEnumerable<string> input, FilterDelegate fDelegate) { foreach (var item in input) { if (fDelegate(item,"S")) yield return item; } } } }
程序運行OK。既然傳入一個委托調用函數可以,那么直接傳入匿名不更簡單,如下:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" }; //匿名方法簡化 IEnumerable<string> query = cities.Filter(delegate(string item,string startWord) { return item.StartsWith(startWord); }); foreach (var city in query) { Console.WriteLine(city); } Console.ReadKey(); } //static bool StringThatStartWith(string item,string startWord) //{ // return item.StartsWith(startWord); //} } //委托作為擴展方法的參數 public static class FilterExtension { public delegate bool FilterDelegate(string item,string startWord); public static IEnumerable<string> Filter(this IEnumerable<string> input, FilterDelegate fDelegate) { foreach (var item in input) { if (fDelegate(item,"S")) yield return item; } } } }
直接用Lambda來簡化吧,如下:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" }; //Lambda //至此實現了同“Where”長相一樣的方法! IEnumerable<string> query = cities.Filter((item, startWord) => item.StartsWith(startWord)); foreach (var city in query) { Console.WriteLine(city); } Console.ReadKey(); } //static bool StringThatStartWith(string item,string startWord) //{ // return item.StartsWith(startWord); //} } //委托作為擴展方法的參數 public static class FilterExtension { public delegate bool FilterDelegate(string item,string startWord); public static IEnumerable<string> Filter(this IEnumerable<string> input, FilterDelegate fDelegate) { foreach (var item in input) { if (fDelegate(item,"S")) yield return item; } } } }
--------
大功告成,利用MS的Where背后的這些C#語法特性,實現了一個類似的Where。
等等..........
貌似忘了一個東西,對Func<>/Action<>委托啊,干嘛自己定義?
重新寫下,如下:
using System; using System.Collections.Generic; using System.Linq; namespace ExtensionQuery { class Program { static void Main(string[] args) { IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" }; //Lambda //至此實現了同“Where”長相一樣的方法! IEnumerable<string> query = cities.Filter((item, startWord) => item.StartsWith(startWord)); foreach (var city in query) { Console.WriteLine(city); } Console.ReadKey(); } //static bool StringThatStartWith(string item,string startWord) //{ // return item.StartsWith(startWord); //} } //委托作為擴展方法的參數 public static class FilterExtension { //public delegate bool FilterDelegate(string item,string startWord); //用Func<>委托取代自定義的委托 public static IEnumerable<string> Filter(this IEnumerable<string> input, Func<string,string,bool> fDelegate) { return input.Where(item => fDelegate(item,"S")); } } }
這下OK了
4.Lambda Expressions
Lambda表達式可以有多個參數、一個參數,或者沒有參數。其參數類型可以隱式或者顯式。示例代碼如下:
- (x, y) => x * y //多參數,隱式類型=> 表達式
- x => x * 5 //單參數, 隱式類型=>表達式
- x => { return x * 5; } //單參數,隱式類型=>語句塊
- (int x) => x * 5 //單參數,顯式類型=>表達式
- (int x) => { return x * 5; } //單參數,顯式類型=>語句塊
- () => Console.WriteLine() //無參數
上述格式都是Lambda表達式的合法格式,在編寫Lambda表達式時,可以忽略參數的類型,因為編譯器能夠根據上下文直接推斷參數的類型。
5.Func<T>和Expression<T>
這個源自園子里的一個博問,問題如下:

Func<TObject, bool>是委托(delegate)
Expression<Func<TObject, bool>>是表達式
Expression編譯后就會變成delegate,才能運行。比如
Expression<Func<int, bool>> ex = x=>x < 100;
Func<int, bool> func = ex.Compile();
然后你就可以調用func:
func(5) //-返回 true
func(200) //- 返回 false
而表達式是不能直接調用的。
參考:http://stackoverflow.com/questions/793571/why-would-you-use-expressionfunct-rather-than-funct
關於EF中用哪個你可以看看這篇文章:Entity Framework - Func引起的數據庫全表查詢
關於如何將多個expression合並為一個可以寫多個where:
.where(expression1).where(expression2)...
運行時EF會自動合並優化的
小結
1.下面兩種寫法都行
IEnumerable<string> cities = new[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" }; //IEnumerable<string> cities = new string[] { "Suzhou", "Wuxi", "Changzhou", "Zhenjiang", "Nanjing", "Shanghai" };//OK also
構建一個可枚舉的集合,這是新特性,逆變與協變(和“父類可以替換子類”相似)。
2.類似“Where”的語法如何自己構建。
3.Lambda表達式的寫法。譬如說{}。
