依賴屬性之“風云再起”
一. 摘要
首先圣殿騎士很高興”WPF 基礎到企業應用系列” 能得到大家的關注、支持和認可。看到很多朋友留言希望加快速度的問題,我會盡力的,對你們的熱情關注也表示由衷的感謝。這段時間更新慢的主要原因是因為忙著用TDD還原MONO的框架,同時也因為一直在研究云計算,所以就拖拖拉拉一直沒有發布后面的文章。由于WPF整個系列是自己的一些粗淺心得和微薄經驗,所以不會像寫書那么面面俱到,如果有不足或者錯誤之處也請大家見諒。在今年之內圣殿騎士會盡量完成”WPF 基礎到企業應用系列”和”云計算之旅系列“,誠然,由于本人才識淺薄,所以熱切希望和大家共勉!
由于依賴屬性是WPF和Silverlight的核心概念,微軟在C\S和B\S平臺上主要精力都放到了WPF和Silverlight技術上,同時Silverlight也是Windows Phone的兩大編程模型之一(另外一種是XNA),所以我們花費了大量的時間和篇幅進行論述。在上一篇WPF基礎到企業應用系列7——深入剖析依賴屬性中,我們首先從依賴屬性基本介紹講起,然后過渡到依賴屬性的優先級、附加屬性、只讀依賴屬性、依賴屬性元數據、依賴屬性回調、驗證及強制值、依賴屬性監聽、代碼段(自動生成) 等相關知識,最后我們模擬一個WPF依賴屬性的實現,由于上篇是根據微軟WPF的BCL源碼剖析的,所以這篇我們就研究一下.NET的跨平臺版本MONO,看下它是怎么來實現這個依賴屬性機制。
二. 本文提綱
· 1.摘要
· 2.本文提綱
· 3.兵馬未動、廢話先行
· 4.依賴屬性續前緣
· 5.引入測試驅動開發
· 6.DependencyProperty測試代碼
· 7.DependencyProperty實現代碼
· 8.DependencyObject測試代碼
· 9.DependencyObject實現代碼
· 10.PropertyMetadata測試代碼
· 11.PropertyMetadata實現代碼
· 12.其他協助類測試代碼
· 13.其他協助類的實現代碼
· 14.回歸并統計覆蓋率
· 15.簡單驗證依賴屬性系統
· 16.本文總結
· 17.相關代碼下載
· 18.系列進度
三. 兵馬未動,廢話先行
在講這篇文章之前,我們先來拉一拉家常,說點題外話,就當進入正餐之前的一些甜點,當然這里主要針對.NET平臺而言:
1,淺談軟件技術的發展趨勢及定位
互聯網的普及應用催生了很多技術的發展與更新,如果仔細深究,你會發現軟件技術的發展趨勢將主要體現在以下四個方面:客戶端軟件開發(其中包括客戶端軟件、游戲、中間件和嵌入式開發等)、Web 開發(包括傳統的Web技術、Web游戲以及一些在線應用)、移動設備軟件開發(主要涉及到手機等移動設備)、云計算開發(公有云、私有云、混合云會逐漸界限清晰,云廠商以及云平臺也會逐漸整合和成熟起來)。就微軟來說,這四個方面主要如下:
◆ 客戶端軟件開發
目前微軟主要有Win32 應用程序、MFC 應用程序、WinForm應用程序和WPF 應用程序作為開發選擇,目前這四種技術還會共存,因為不同的需求以及不同的人群都有不同的需要。當然WPF借助于其強大的功能和迅猛的發展速度很快會成為首選,這個是值得肯定的。
◆ Web 開發
在WEB方面微軟主要有ASP.NET、ASP.NET MVC、Silverlight三種技術,ASP.NET技術已經發展了多年,在未來的很長一段時間內還會是主流,同時結合Silverlight作為局部和整體應用效果都還很不錯,所以這也是很多企業的首選。ASP.NET MVC在目前來說應用還不是特別廣泛,不過用過之后感覺也還不錯,只是還需要一段時間的適應過程而已。Silverlight在構建局部應用和整站應用都發揮了不錯的優勢,在Windows Phone中也表現得不錯,所以這個技術將會一直熱下去。
◆ 移動設備軟件開發
移動設備方面可謂是現在眾廠商競爭最激烈的市場之一,也是傳統技術和新型技術的主要戰場之一。微軟現在主推的Windows Phone開發主要包括Silverlight和XNA兩種技術,Windows Phone開發逐漸變得和ASP.NET開發一樣簡單,這也是微軟的一個目標。
◆ 云計算開發
云計算現在基本上成了互聯網的第一大熱門詞,不管是軟件為主導的企業,還是以硬件為主導的企業,都卷入了這場紛爭與革命。微軟的云平臺——Windows Azure Platform,它是微軟完整的云計算平臺,目前包含了如下三大部分(Windows Azure:運行在云中的操作系統,對于用戶來說是虛擬且透明的,其中提供了Compute(計算),Storage(存儲),以及Manage(管理)這三個主要功能及其底層服務,使用起來相當的便捷。SQL Azure:運行于云中的一個關系數據庫,和SQL Server 2008類似,但是在功能上還沒有那么強大。AppFabric:全名是Windows Azure platform AppFabric,提供了訪問控制、服務總線等服務,主要用于把基礎應用連接到云中)。
其實把這四個方面總結起來就是傳說中的微軟“三屏一云”戰略,從中也可以看出微軟逍遙于天地,縱橫于宇內,嘯傲于世間,雄霸于大地的梟雄戰略!
2,淺談微軟跨平臺與MONO
在談之前我們先看一下什么是MONO?MONO項目是由Ximian發起、Miguel de lcaza領導、Novell公司主持的項目。它是一個致力于開創.NET在Linux,FreeBSD,Unix,Mac OS X和Solaris等其他平臺使用的開源工程。它包含了一個C#語言的編譯器,一個CLR的運行時,和一組類庫,并逐漸實現了 ADO.NET、ASP.NET、WinForm、Silverlight(可惜沒有實現強大的WPF),能夠使得開發人員在其他平臺用C#開發程序。
◆ 值得看好的地方:
1,跨平臺:開創.NET在Linux,FreeBSD,Unix,Mac OS X和Solaris等其他平臺使用,這是微軟沒有實現的,但是MONO進行了補充,所以值得看好。
2,開源:不論使用什么技術,大家似乎都希望能夠用開源的產品,一方面是考慮到技術的可控性和可維護性;另一方面則是考慮到安全性,當然在另一個角度也是可以學習到其中的一些技術和思想,所以大家對開源總是報以歡迎的態度。
3,不同的方式實現.NET框架:由于微軟對技術申請了專利,所以MONO不能盲目的模仿,對很多細節都改用自己的方式進行了實現,所以我們也可以學到很多不一樣的實現方式。
4,持續更新:MONO從一開始到現在始終在更新,其中包括bug修復、版本升級、增加新的功能及應用,所以相信它會在不斷的更新中更加完善。
◆ 不足之處:
1.模仿但要避免專利:由于是模仿微軟.NET平臺,但因為微軟對代碼申請了專利,所以MONO只能采用其它實現方式來實現同樣的功能,這樣一來很多地方就會實現得很累贅,效率也會受損。
2.沒有擺脫實驗產品的頭銜:由于它目前的使用比較低,所以信息反饋和持續改進就做得比較弱,這也是目前功能完善得比較慢的原因之一吧。
3,功能還需要完善:一些主要功能還未實現,如作為Windows平臺最基礎的COM和COM+功能沒有保存,像MSMQ等消息隊列,消息傳送的功能也沒有實現,對ADO.NET、XML等核心功能效率有待提升,對BCL庫代碼也有很多需要優化的地方,強大的WPF也沒有引入。
4.效率和用戶體驗還有待提升。
◆ 與微軟之間的關系
微軟與MONO之間的關系也一直處于不冷不熱的狀態,沒有明確的反對,也沒有明確的支持,究其原因筆者認為主要有以下兩點:
1,微軟帶來最大收益的產品仍舊是Windows操作系統和Office等軟件,微軟在其他領域盈利都沒有這兩大產品來得直接。而.NET作為微軟的強大開發平臺,是不希望落在其他平臺上運行的,這樣就會削弱Windows操作系統和Office等軟件的市場占有率,所以讓.NET跨平臺對微軟來說是一件舍本求末的事情,這也是微軟不主張.NET運行于其他平臺的主要原因,你想微軟是一個以技術為主導的公司,任何IT市場都會有它的身影,如果想讓.NET跨平臺,那豈不是一件很輕而易舉的事情嗎?
2,由于MONO還沒有成熟,在很多方面都表現得像一個實驗室產品,在根本上沒有對微軟構成威脅,況且在外界質疑.NET是否能跨平臺的時候,還有一個現身的說法,所以微軟也不會明確的反對和支持。
◆ 總結
雖然目前來說MONO喜憂參半,但優點始終要大于缺點,畢竟每一個框架或者產品都是慢慢不斷改進而完善的,更何況開源必將是未來的一個趨勢,所以我們有理由也有信心期待它接下來的發展。
3,談談源碼研究與TDD
大家都有一個共識:如果你想研究某個框架或者工具的源碼,那先必須熟練使用它,熟練之后自然就有一種研究它的沖動,但是往往這個框架或工具比較龐大,很不容易下手,一個很不錯的方法就是使用TDD。我們都知道TDD的基本思想就是在開發功能代碼之前,先編寫測試代碼。也就是說在明確要開發某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關的代碼滿足這些測試用例。然后循環進行添加其他功能,直到完全部功能的開發,在此過程中我們可以借助一些工具來協助。比如我們現在要研究Nhibernate,那么我們首先要熟練它的一些功能,然后從一個點出發慢慢編寫單元測試,然后逐漸完善代碼,最后直至完成框架的搭建,這樣會給我們帶來莫大的驅動力和成就感。除了微軟的BCL(Base Class Library)和企業庫以外,大家還可以用TDD來試試還原以下的任一開源代碼:
Spring.NET(http://www.springframework.net/)、Castle(http://www.castleproject.org)、log4net(http://logging.apache.org/log4net/)、
NHibernate(http://www.hibernate.org/343.html)、iBATIS.NET(http://ibatis.apache.org)、Caliburn(http://caliburn.codeplex.com/)、
MVVM Light Toolkit(http://mvvmlight.codeplex.com/)、Prism(http://compositewpf.codeplex.com/)、MONO源碼(www.mono-project.com)
四. 依賴屬性續前緣
大家都知道WPF和Silverlight帶來了很多新的特性,其中一大亮點是引入了一種新的屬性機制——依賴屬性。依賴屬性基本應用在了WPF的所有需要設置屬性的元素。依賴屬性根據多個提供對象來決定它的值(可以是動畫、父類元素、綁定、樣式和模板等),同時這個值也能及時響應變化。所以WPF擁有了依賴屬性后,代碼寫起來就比較得心應手,功能實現上也變得非常容易了。如果沒有依賴屬性,我們將不得不編寫大量的代碼。依賴屬性在WPF中用得非常廣泛,具體在以下幾個方面中表現得尤為突出:
◆ UI的強大屬性體系
◆ Property value inheritance(值繼承)
◆ Metadata(強大的元數據)
◆ 屬性變化通知,限制、驗證
◆ Resources(資源)
◆ Data binding(數據綁定)
◆ Styles、Template(樣式、模板和風格)
◆ 路由事件、附加事件、附加行為乃至命令
◆ Animations、3D(動畫和3D)
◆ WPF Designer Integration(WPF設計、開發集成)
在上一篇WPF基礎到企業應用系列7——深入剖析依賴屬性中,我們對依賴屬性做了較詳細的介紹,那么下面我們就簡單回顧一下,其實依賴屬性的實現很簡單,只要做以下步驟就可以實現:
◆ 第一步: 讓所在類型繼承自 DependencyObject基類,在WPF中,我們仔細觀察框架的類圖結構,你會發現幾乎所有的 WPF 控件都間接繼承自DependencyObject類型。
◆ 第二步:使用 public static 聲明一個 DependencyProperty的變量,該變量才是真正的依賴屬性 ,看源碼就知道這里其實用了簡單的單例模式的原理進行了封裝(構造函數私有),只暴露Register方法給外部調用。
◆ 第三步:在靜態構造函數中完成依賴屬性的元數據注冊,并獲取對象引用,看代碼就知道是把剛才聲明的依賴屬性放入到一個類似于容器的地方,沒有講實現原理之前,請容許我先這么陳述。
◆ 第四步:在前面的三步中,我們完成了一個依賴屬性的注冊,那么我們怎樣才能對這個依賴屬性進行讀寫呢?答案就是提供一個依賴屬性的實例化包裝屬性,通過這個屬性來實現具體的讀寫操作。
根據前面的四步操作,我們就可以寫出下面的代碼:
1: public class SampleDPClass : DependencyObject
2: {
3: //聲明一個靜態只讀的DependencyProperty字段
4: public static readonly DependencyProperty SampleProperty;
5:
6: static SampleDPClass()
7: {
8: //注冊我們定義的依賴屬性Sample
9: SampleProperty = DependencyProperty.Register("Sample", typeof(string), typeof(SampleDPClass),
10: new PropertyMetadata("Knights Warrior!", OnValueChanged));
11: }
12:
13: private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
14: {
15: //當值改變時,我們可以在此做一些邏輯處理
16: }
17:
18: //屬性包裝器,通過它來讀取和設置我們剛才注冊的依賴屬性
19: public string Sample
20: {
21: get { return (string)GetValue(SampleProperty); }
22: set { SetValue(SampleProperty, value); }
23: }
24: }
通過上面的例子可以看出,我們一般.NET屬性是直接對類的一個私有屬性進行封裝,所以讀取值的時候,也就是直接讀取這個字段;而依賴屬性則是通過調用繼承自DependencyObject的GetValue()和SetValue來進行操作,它實際存儲在DependencyProperty的一個IDictionary的鍵-值配對字典中,所以一條記錄中的鍵(Key)就是該屬性的HashCode值,而值(Value)則是我們注冊的DependencyProperty。 回顧了一些基礎知識,那我們下面就開始今天的依賴屬性系統TDD之旅。