C#的变迁史03 - C# 3.0篇
C# 3.0 (.NET 3.5, VS2008)
第三代C#在語法元素基本完備的基礎上提供了全新的開發工具和集合數據查詢方式,極大的方便了開發。
1. WPF,WCF,WF
這3個工程類型奠定了新一代.NET開發的客戶端模型,通信模型,工作流模型。
WPF即將取代Winform成為新一代的桌面程序開發工具,控件與代碼的超低耦合給開發帶來了極大的方便,脫胎于MVC的MVVM模式也展現了極強的生命力。WCF即將融合原來的Web Service的各種提供方式,徹底屏蔽掉各種細節,開啟Service服務的篇章。WF似乎使用的比較少,目前不做評論。
這3個概念每個都可以寫上幾本書,這里就不出丑了。
?
2. Lambda表達式
Lambda 表達式是一種可用于創建委托或表達式樹類型的匿名函數。 通過使用 lambda 表達式,可以創建可作為參數傳遞或作為函數調用值返回的本地函數。 Lambda 表達式主戰場是 Linq?查詢表達式,這個在下面會提及;由于Lambda表達式是創建委托和表達式樹的,所以它在除Linq之外的地方使用也很廣泛,使用Lambda表達式可以寫出相當優雅的委托代碼。
若要創建 Lambda 表達式,需要在 Lambda 運算符 => 左側指定輸入參數(如果有),然后在另一側輸入表達式或語句塊。 例如,lambda 表達式 x => x * x 指定名為 x 的參數并返回 x 的平方值。
左側的參數列表可以帶參數類型,也可以不帶。指定參數類型時通常是因為右側的語句塊中使用了該類型的相關方法;當不帶參數類型的時候,就由編譯器根據上下文去推斷,推斷成立則沒問題,推斷不成立則編譯錯誤。左側的參數列表需要帶"()",但是當只有一個無類型的參數時可省略。
右側的如果是單個表達式的話可以不帶"{}"和";",是語句塊的話則要加上。
你可以將此表達式分配給委托類型:
delegate int del(int i); static void Main(string[] args) {del myDelegate = x => x * x;int j = myDelegate(5); //j = 25 }當然也可以用于創建表達式樹類型:
using System.Linq.Expressions;namespace ConsoleApplication1 {class Program{static void Main(string[] args){Expression<del> myET = x => x * x;}} }表達式樹是一種動態創建表達式的工具,表達式樹可以編譯和運行;使用它可以動態的創建表達式,它的使用場合主要是在LINQ中區創建動態的查詢條件表達式。關于表達式樹,有興趣的同學可以參考MSDN:http://msdn.microsoft.com/zh-cn/library/bb397951.aspx?。
總的來說,Lambda表達式是基于delegate又高于delegate的東東,青出于藍而勝于藍。它更加弱化的約束極大了豐富了表達式的含義,所謂描述越抽象,意義越豐富嘛!難怪抽象派藝術家的作品生命力都很強。在C#中,約束最強的應該就是繼承(包括類的繼承,接口的實現),它要求子類擴展父類或接口的時候方法時簽名要完全匹配,包括返回值類型,函數名,參數類型等;約束次之的是delegate,它不再要求方法名相同了,只要求返回值類型與參數類型相同;到了lambda表達式了,約束更加弱化,連參數與返回值的類型都不要求完全匹配了,難怪有人說如果當初現有lambda表達式的話,就不會有匿名函數的語法了。
?
3. 對象初始化器和集合初始化器
直接初始化的時候就可以初始化是很方便的事,這個終于有了,雖說是在LINQ中使用最多,但是在其它場合使用對象初始化器和集合初始化器編程還是顯得特別優雅。這個比較簡單不多說了,看例子:
// 基本用法: User user =new User { Id = 1, Name ="AA", Age = 22 }; //嵌套使用: User user =new User {Id = 1,Name ="AA",Age = 22,Address = new Address{City ="NanJing",Zip = 21000} }; //類似于對象初始化器初始化一個對象,集合初始化器初始化一個集合, //一句話,有了它你就不用在將元素通過Add逐個添加了: //基本使用: List<int> num =new List<int> { 0, 1, 2, 6, 7, 8, 9 }; //結合對象初始化器,我們可以寫出如下簡潔的代碼: List<User> user =new List<User>{new User{Id=1,Name="AA",Age=22},new User{Id=2,Name="BB",Age=25}, };?
4. 匿名類型
很多時候,我們需要使用一個臨時的對象,按通常的做法,我們要先定義一個類吧,這樣才能實例化這個類得到對象,這樣實在是太累了;而且往往這樣的對象只需要使用在局部的場合,使用以后就不再使用了,例如從數據庫中查詢出來的臨時結果。在3.0中,我們終于不再需要預先定義一個類型了,CLR會提供一種形式讓你動態的生成一個無類型的對象。
匿名類型提供了一種方便的方法,可用來將一組只讀屬性封裝到單個對象中,而無需首先顯式定義一個類型。 類型名由編譯器生成,并且不能在源代碼級使用。 每個屬性的類型由編譯器推斷。
在看具體的例子之前,先看一個新的關鍵字:var。這個關鍵字在Javascript中使用廣泛,在C#中使用var聲明一個局部變量(只能在函數中使用var定義變量,這種變量官方稱之為隱式類型局部變量)時,編譯器會自動根據其賦值語句推斷這個局部變量的類型。賦值以后,這個變量的類型也就確定而不可以再進行更改。但是這個var關鍵字最主要的用途是去生成匿名類型,比如表示一個Linq查詢的結果。這個結果可能是ObjectQuery<T>或IQueryable<T>類型的對象,也可能是一個簡單的實體類型的對象。這時使用var聲明這個對象可以節省很多代碼書寫上的時間。
看一下匿名類型的使用方式:
var people = new { Name="AA", Age = 10 }; Console.WriteLine(people.Name);需要注意的是,上面創建的people的Name和Age是只讀的,不能去修改。
5. 擴展方法
Linq擴展了原來的IEnumerable得到IQueryable,如何自然的融入原來的集合中卻是一個問題,有了這個語法糖,只要符合規定的語法,引入定義擴展方法的namespace,新添加的方法就像是對象原來就有的方法那樣方便使用,這樣就在不破壞原對象封裝性的前提下給該對象添加了新的行為,還是蠻符合面向對象Open-Close原則的。
看網上的一個例子:
using System; using System.Text.RegularExpressions;namespace ConsoleApplication3 {class Program{static void Main(string[] args){string email = "someone@somewhere.com";Console.WriteLine(email.IsValidEmailAddress());}}public static class Extensions{public static bool IsValidEmailAddress(this string s){Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");return regex.IsMatch(s);}} }如上代碼所示,擴展方法為一靜態方法,聲明于一個靜態類,其參數前加上一個this關鍵字,參數的類型表示這個擴展方法要對這個類型進行擴展。如上述代碼表示其要對字符串類型進行擴展。使用起來也很方便,所有的string類型現在都多了一個叫IsValidEmailAddress的方法。
?
6.?Linq?
當所有的配角都到齊的時候,主角之一的Linq?(Language Integrated Query,語言集成查詢)應該要亮相了,它是3.0時代最為強悍的工具,從此查詢集合數據有了最為便捷的方式,3.0中上面的許多特性基本上都是為了Linq?服務的。
Linq主要包含4個組件——Linq to Objects、Linq to XML、Linq to DataSet 和Linq to SQL,這4個組件對應4種查詢的對象:內存集合中的對象,XML,SQL和Dataset。在我的經歷中,我用的最多的是前三個,所以這里主要就是總結前三種查詢。
在Linq之前查詢數據,基本的方式就是循環遍歷,按照條件比較,把得到的結果放到臨時集合中。Linq徹底簡化了這個過程,使用了簡單的方式完成了查詢。不管查詢上述的哪些對象,方式其實還都是一樣的:要么使用查詢表達式,要么使用擴展方法。
以查詢內存集合中的對象為例,看一下兩種使用方式:
using System.Linq; List<string> collection = new List<string>(); for (int i = 0; i < 10; i++) {collection.Add("A" + i.ToString()); }// 創建查詢表達式來獲得序號為偶數的元素???? var queryResults = from s in collectionlet index = int.Parse(s.Substring(1))where index % 2 == 0select s;// 使用擴展方法來獲得序號為偶數的元素???? var queryResults1 = collection.Where(item => {int index = int.Parse(item.Substring(1));return index % 2 == 0; }); // 輸出查詢結果???? foreach (string s in queryResults) {Console.WriteLine(s); }查詢Xml的方式比較相似,下面來自網上的例子比較了使用原來Class訪問Xml的方式,和使用XLinq訪問Xml的方式:
using System.Xml; using System.Xml.Linq;static void Main(string[] args)???? {???? ??? Console.WriteLine("使用XPath來對XML文件查詢,查詢結果為:");???? OldLinqToXMLQuery();???? Console.WriteLine("使用Linq方法來對XML文件查詢,查詢結果為:");???? UsingLinqLinqtoXMLQuery();???? Console.ReadKey();???? }?????? // 初始化XML數據???? private static string xmlString =????? "<Persons>"+???? "<Person Id='1'>"+???? "<Name>張三</Name>"+???? "<Age>18</Age>"+???? "</Person>" +???? "<Person Id='2'>"+???? "<Name>李四</Name>"+???? "<Age>19</Age>"+???? "</Person>"+???? "<Person Id='3'>" +???? "<Name>王五</Name>" +???? "<Age>22</Age>" +???? "</Person>"+???? "</Persons>";???? // 使用XPath方式來對XML文件進行查詢???? private static void OldLinqToXMLQuery()???? {???? // 導入XML文件???? XmlDocument xmlDoc = new XmlDocument();???? xmlDoc.LoadXml(xmlString);???? // 創建查詢XML文件的XPath???? string xPath = "/Persons/Person";???? // 查詢Person元素???? XmlNodeList querynodes = xmlDoc.SelectNodes(xPath);???? foreach (XmlNode node in querynodes)???? {???? // 查詢名字為李四的元素???? foreach (XmlNode childnode in node.ChildNodes)???? {???? if (childnode.InnerXml == "李四")???? {???? Console.WriteLine("姓名為: "+childnode.InnerXml + "? Id 為:" + node.Attributes["Id"].Value);???? }???? }???? }???? ???????? }???? // 使用Linq 來對XML文件進行查詢???? private static void UsingLinqLinqtoXMLQuery()???? {???? // 導入XML???? XElement xmlDoc = XElement.Parse(xmlString);???? // 創建查詢,獲取姓名為“李四”的元素???? var queryResults = from element in xmlDoc.Elements("Person")???? where element.Element("Name").Value == "李四"select element;???? // 輸出查詢結果???? foreach (var xele in queryResults)???? {???? Console.WriteLine("姓名為: " + xele.Element("Name").Value + "? Id 為:" + xele.Attribute("Id").Value);???? }???? }?需要注意的是使用上面Linq查詢得到的結果,只有當使用foreach等方法去遍歷的時候查詢才會真正的執行并返回結果。
有的同學很快就發現了,這些關鍵字怎么看都像是Sql語句的關鍵字,確實,這就是語言集成查詢的真正含義,查詢語句可以直接在編譯的時候確定語法上正確性,這一點體現最完全的就是Linq to Sql,通常也稱為DLinq。Sql基本所有的關鍵字都可以在C#中找到想對應的關鍵字,所以Sql基本所有的操作在Linq中都有,比如:查詢、排序、分組、增加和刪除等等。這個可以通過查詢MSDN(http://msdn.microsoft.com/zh-cn/library/bb397676.aspx)或者學習一些優秀的Blog(http://www.cnblogs.com/lifepoem/archive/2011/12/16/2288017.html)?來熟悉其語法。
在.NET下與數據庫交互時,為了從對象級別處理問題,必須對數據庫進行抽象的到對應的對象,這個就是ORM(對象關系映射,Object/Relation Mapping)的過程。ORM按照字面的意思理解即可:ORM的過程就是把數據庫中的概念,比如數據庫,表,字段等,處理成語言中的對象,然后在對象級別上完成數據庫的常用操作,這些操作會通關相關的機制自動的反映到數據庫中。這是一個抽象的過程,這樣做的好處就是處理的程序不用考慮數據庫實際操作上的細節,只要在語言層次上完成相關的操作,數據庫自然就會被更新。
在.NET下ORM的手段主要是兩種:DLinq和Entity Framework。這兩種手段的目標都是一致的,就是ORM的那個過程,但是處理的細節(比如抽象的級別,支持的數據庫,支持的程度,是否繼續維護等等)有些不同,也就導致了兩者不同的命運。總的來說,Entity Framework是重量級的工具,支持多種數據庫,特性更多,更加抽象,更加靈活,效率更好一點,而且新版對linq的語法支持也越來越完善(DLinq不再添加新特性了,前途很明顯),這真是居家旅行殺人越貨必備之首選啊。
兩者的對比網上也很多,比如:http://blog.163.com/kunkun0921@126/blog/static/169204332201401605839384/?。
這里想多啰嗦一句的就是Entity Framework支持所謂的Database First和Code First兩種開發方式,前置是先設計數據庫,后映射對象;后者是先設計對象,后生成數據庫。這些開發方式還是相當靈活的,而且在VS的命令行中生成、更新數據庫都有相關的命令,使用起來是相當的方便。
?
此外還有一些好的學習網站:
http://www.tuicool.com/topics/11050019
http://www.entlib.net/?cat=31
http://www.cnblogs.com/lsxqw2004/category/266012.html
總結
以上是生活随笔為你收集整理的C#的变迁史03 - C# 3.0篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有逾期记录还能申请信用卡吗
- 下一篇: 怎么填申请表才能申请到大额信用卡