|
一. 摘要
首先圣殿騎士很高興”WPF 基礎(chǔ)到企業(yè)應(yīng)用系列” 能得到大家的關(guān)注、支持和認(rèn)可。看到很多朋友留言希望加快速度的問題,我會(huì)盡力的,對(duì)你們的熱情關(guān)注也表示由衷的感謝。這段時(shí)間更新慢的主要原因是因?yàn)槊χ肨DD還原MONO的框架,同時(shí)也因?yàn)橐恢痹谘芯吭朴?jì)算,所以就拖拖拉拉一直沒有發(fā)布后面的文章。由于WPF整個(gè)系列是自己的一些粗淺心得和微薄經(jīng)驗(yàn),所以不會(huì)像寫書那么面面俱到,如果有不足或者錯(cuò)誤之處也請(qǐng)大家見諒。在今年之內(nèi)圣殿騎士會(huì)盡量完成”WPF 基礎(chǔ)到企業(yè)應(yīng)用系列”和”云計(jì)算之旅系列“,誠(chéng)然,由于本人才識(shí)淺薄,所以熱切希望和大家共勉!
由于依賴屬性是WPF和Silverlight的核心概念,微軟在C/S和B/S平臺(tái)上主要精力都放到了WPF和Silverlight技術(shù)上,同時(shí)Silverlight也是Windows Phone的兩大編程模型之一(另外一種是XNA),所以我們花費(fèi)了大量的時(shí)間和篇幅進(jìn)行論述。在上一篇WPF基礎(chǔ)到企業(yè)應(yīng)用系列7——深入剖析依賴屬性中,我們首先從依賴屬性基本介紹講起,然后過(guò)渡到依賴屬性的優(yōu)先級(jí)、附加屬性、只讀依賴屬性、依賴屬性元數(shù)據(jù)、依賴屬性回調(diào)、驗(yàn)證及強(qiáng)制值、依賴屬性監(jiān)聽、代碼段(自動(dòng)生成) 等相關(guān)知識(shí),最后我們模擬一個(gè)WPF依賴屬性的實(shí)現(xiàn),由于上篇是根據(jù)微軟WPF的BCL源碼剖析的,所以這篇我們就研究一下.NET的跨平臺(tái)版本MONO,看下它是怎么來(lái)實(shí)現(xiàn)這個(gè)依賴屬性機(jī)制。
二. 本文提綱
· 1.摘要
· 2.本文提綱
· 3.兵馬未動(dòng)、廢話先行
· 4.依賴屬性續(xù)前緣
· 5.引入測(cè)試驅(qū)動(dòng)開發(fā)
· 6.DependencyProperty測(cè)試代碼
· 7.DependencyProperty實(shí)現(xiàn)代碼
· 8.DependencyObject測(cè)試代碼
· 9.DependencyObject實(shí)現(xiàn)代碼
· 10.PropertyMetadata測(cè)試代碼
· 11.PropertyMetadata實(shí)現(xiàn)代碼
· 12.其他協(xié)助類測(cè)試代碼
· 13.其他協(xié)助類的實(shí)現(xiàn)代碼
· 14.回歸并統(tǒng)計(jì)覆蓋率
· 15.簡(jiǎn)單驗(yàn)證依賴屬性系統(tǒng)
· 16.本文總結(jié)
· 17.相關(guān)代碼下載
· 18.系列進(jìn)度
三. 兵馬未動(dòng),廢話先行
在講這篇文章之前,我們先來(lái)拉一拉家常,說(shuō)點(diǎn)題外話,就當(dāng)進(jìn)入正餐之前的一些甜點(diǎn),當(dāng)然這里主要針對(duì).NET平臺(tái)而言:
1,淺談軟件技術(shù)的發(fā)展趨勢(shì)及定位
互聯(lián)網(wǎng)的普及應(yīng)用催生了很多技術(shù)的發(fā)展與更新,如果仔細(xì)深究,你會(huì)發(fā)現(xiàn)軟件技術(shù)的發(fā)展趨勢(shì)將主要體現(xiàn)在以下四個(gè)方面:客戶端軟件開發(fā)(其中包括客戶端軟件、游戲、中間件和嵌入式開發(fā)等)、Web 開發(fā)(包括傳統(tǒng)的Web技術(shù)、Web游戲以及一些在線應(yīng)用)、移動(dòng)設(shè)備軟件開發(fā)(主要涉及到手機(jī)等移動(dòng)設(shè)備)、云計(jì)算開發(fā)(公有云、私有云、混合云會(huì)逐漸界限清晰,云廠商以及云平臺(tái)也會(huì)逐漸整合和成熟起來(lái))。就微軟來(lái)說(shuō),這四個(gè)方面主要如下:
◆ 客戶端軟件開發(fā)
目前微軟主要有Win32 應(yīng)用程序、MFC 應(yīng)用程序、WinForm應(yīng)用程序和WPF 應(yīng)用程序作為開發(fā)選擇,目前這四種技術(shù)還會(huì)共存,因?yàn)椴煌男枨笠约安煌娜巳憾加胁煌男枰.?dāng)然WPF借助于其強(qiáng)大的功能和迅猛的發(fā)展速度很快會(huì)成為首選,這個(gè)是值得肯定的。
◆ Web 開發(fā)
在WEB方面微軟主要有ASP.NET、ASP.NET MVC、Silverlight三種技術(shù),ASP.NET技術(shù)已經(jīng)發(fā)展了多年,在未來(lái)的很長(zhǎng)一段時(shí)間內(nèi)還會(huì)是主流,同時(shí)結(jié)合Silverlight作為局部和整體應(yīng)用效果都還很不錯(cuò),所以這也是很多企業(yè)的首選。ASP.NET MVC在目前來(lái)說(shuō)應(yīng)用還不是特別廣泛,不過(guò)用過(guò)之后感覺也還不錯(cuò),只是還需要一段時(shí)間的適應(yīng)過(guò)程而已。Silverlight在構(gòu)建局部應(yīng)用和整站應(yīng)用都發(fā)揮了不錯(cuò)的優(yōu)勢(shì),在Windows Phone中也表現(xiàn)得不錯(cuò),所以這個(gè)技術(shù)將會(huì)一直熱下去。
◆ 移動(dòng)設(shè)備軟件開發(fā)
移動(dòng)設(shè)備方面可謂是現(xiàn)在眾廠商競(jìng)爭(zhēng)最激烈的市場(chǎng)之一,也是傳統(tǒng)技術(shù)和新型技術(shù)的主要戰(zhàn)場(chǎng)之一。微軟現(xiàn)在主推的Windows Phone開發(fā)主要包括Silverlight和XNA兩種技術(shù),Windows Phone開發(fā)逐漸變得和ASP.NET開發(fā)一樣簡(jiǎn)單,這也是微軟的一個(gè)目標(biāo)。
◆ 云計(jì)算開發(fā)
云計(jì)算現(xiàn)在基本上成了互聯(lián)網(wǎng)的第一大熱門詞,不管是軟件為主導(dǎo)的企業(yè),還是以硬件為主導(dǎo)的企業(yè),都卷入了這場(chǎng)紛爭(zhēng)與革命。微軟的云平臺(tái)——Windows Azure Platform,它是微軟完整的云計(jì)算平臺(tái),目前包含了如下三大部分(Windows Azure:運(yùn)行在云中的操作系統(tǒng),對(duì)于用戶來(lái)說(shuō)是虛擬且透明的,其中提供了Compute(計(jì)算),Storage(存儲(chǔ)),以及Manage(管理)這三個(gè)主要功能及其底層服務(wù),使用起來(lái)相當(dāng)?shù)谋憬荨QL Azure:運(yùn)行于云中的一個(gè)關(guān)系數(shù)據(jù)庫(kù),和SQL Server 2008類似,但是在功能上還沒有那么強(qiáng)大。AppFabric:全名是Windows Azure platform AppFabric,提供了訪問控制、服務(wù)總線等服務(wù),主要用于把基礎(chǔ)應(yīng)用連接到云中)。
其實(shí)把這四個(gè)方面總結(jié)起來(lái)就是傳說(shuō)中的微軟“三屏一云”戰(zhàn)略,從中也可以看出微軟逍遙于天地,縱橫于宇內(nèi),嘯傲于世間,雄霸于大地的梟雄戰(zhàn)略!
2,淺談微軟跨平臺(tái)與MONO
在談之前我們先看一下什么是MONO?MONO項(xiàng)目是由Ximian發(fā)起、Miguel de lcaza領(lǐng)導(dǎo)、Novell公司主持的項(xiàng)目。它是一個(gè)致力于開創(chuàng).NET在Linux,F(xiàn)reeBSD,Unix,Mac OS X和Solaris等其他平臺(tái)使用的開源工程。它包含了一個(gè)C#語(yǔ)言的編譯器,一個(gè)CLR的運(yùn)行時(shí),和一組類庫(kù),并逐漸實(shí)現(xiàn)了 ADO.NET、ASP.NET、WinForm、Silverlight(可惜沒有實(shí)現(xiàn)強(qiáng)大的WPF),能夠使得開發(fā)人員在其他平臺(tái)用C#開發(fā)程序。
◆ 值得看好的地方:
1,跨平臺(tái):開創(chuàng).NET在Linux,F(xiàn)reeBSD,Unix,Mac OS X和Solaris等其他平臺(tái)使用,這是微軟沒有實(shí)現(xiàn)的,但是MONO進(jìn)行了補(bǔ)充,所以值得看好。
2,開源:不論使用什么技術(shù),大家似乎都希望能夠用開源的產(chǎn)品,一方面是考慮到技術(shù)的可控性和可維護(hù)性;另一方面則是考慮到安全性,當(dāng)然在另一個(gè)角度也是可以學(xué)習(xí)到其中的一些技術(shù)和思想,所以大家對(duì)開源總是報(bào)以歡迎的態(tài)度。
3,不同的方式實(shí)現(xiàn).NET框架:由于微軟對(duì)技術(shù)申請(qǐng)了專利,所以MONO不能盲目的模仿,對(duì)很多細(xì)節(jié)都改用自己的方式進(jìn)行了實(shí)現(xiàn),所以我們也可以學(xué)到很多不一樣的實(shí)現(xiàn)方式。
4,持續(xù)更新:MONO從一開始到現(xiàn)在始終在更新,其中包括bug修復(fù)、版本升級(jí)、增加新的功能及應(yīng)用,所以相信它會(huì)在不斷的更新中更加完善。
◆ 不足之處:
1.模仿但要避免專利:由于是模仿微軟.NET平臺(tái),但因?yàn)槲④泴?duì)代碼申請(qǐng)了專利,所以MONO只能采用其它實(shí)現(xiàn)方式來(lái)實(shí)現(xiàn)同樣的功能,這樣一來(lái)很多地方就會(huì)實(shí)現(xiàn)得很累贅,效率也會(huì)受損。
2.沒有擺脫實(shí)驗(yàn)產(chǎn)品的頭銜:由于它目前的使用比較低,所以信息反饋和持續(xù)改進(jìn)就做得比較弱,這也是目前功能完善得比較慢的原因之一吧。
3,功能還需要完善:一些主要功能還未實(shí)現(xiàn),如作為Windows平臺(tái)最基礎(chǔ)的COM和COM+功能沒有保存,像MSMQ等消息隊(duì)列,消息傳送的功能也沒有實(shí)現(xiàn),對(duì)ADO.NET、XML等核心功能效率有待提升,對(duì)BCL庫(kù)代碼也有很多需要優(yōu)化的地方,強(qiáng)大的WPF也沒有引入。
4.效率和用戶體驗(yàn)還有待提升。
◆ 與微軟之間的關(guān)系
微軟與MONO之間的關(guān)系也一直處于不冷不熱的狀態(tài),沒有明確的反對(duì),也沒有明確的支持,究其原因筆者認(rèn)為主要有以下兩點(diǎn):
1,微軟帶來(lái)最大收益的產(chǎn)品仍舊是Windows操作系統(tǒng)和Office等軟件,微軟在其他領(lǐng)域盈利都沒有這兩大產(chǎn)品來(lái)得直接。而.NET作為微軟的強(qiáng)大開發(fā)平臺(tái),是不希望落在其他平臺(tái)上運(yùn)行的,這樣就會(huì)削弱Windows操作系統(tǒng)和Office等軟件的市場(chǎng)占有率,所以讓.NET跨平臺(tái)對(duì)微軟來(lái)說(shuō)是一件舍本求末的事情,這也是微軟不主張.NET運(yùn)行于其他平臺(tái)的主要原因,你想微軟是一個(gè)以技術(shù)為主導(dǎo)的公司,任何IT市場(chǎng)都會(huì)有它的身影,如果想讓.NET跨平臺(tái),那豈不是一件很輕而易舉的事情嗎?
2,由于MONO還沒有成熟,在很多方面都表現(xiàn)得像一個(gè)實(shí)驗(yàn)室產(chǎn)品,在根本上沒有對(duì)微軟構(gòu)成威脅,況且在外界質(zhì)疑.NET是否能跨平臺(tái)的時(shí)候,還有一個(gè)現(xiàn)身的說(shuō)法,所以微軟也不會(huì)明確的反對(duì)和支持。
◆ 總結(jié)
雖然目前來(lái)說(shuō)MONO喜憂參半,但優(yōu)點(diǎn)始終要大于缺點(diǎn),畢竟每一個(gè)框架或者產(chǎn)品都是慢慢不斷改進(jìn)而完善的,更何況開源必將是未來(lái)的一個(gè)趨勢(shì),所以我們有理由也有信心期待它接下來(lái)的發(fā)展。
3,談?wù)勗创a研究與TDD
大家都有一個(gè)共識(shí):如果你想研究某個(gè)框架或者工具的源碼,那先必須熟練使用它,熟練之后自然就有一種研究它的沖動(dòng),但是往往這個(gè)框架或工具比較龐大,很不容易下手,一個(gè)很不錯(cuò)的方法就是使用TDD。我們都知道TDD的基本思想就是在開發(fā)功能代碼之前,先編寫測(cè)試代碼。也就是說(shuō)在明確要開發(fā)某個(gè)功能后,首先思考如何對(duì)這個(gè)功能進(jìn)行測(cè)試,并完成測(cè)試代碼的編寫,然后編寫相關(guān)的代碼滿足這些測(cè)試用例。然后循環(huán)進(jìn)行添加其他功能,直到完全部功能的開發(fā),在此過(guò)程中我們可以借助一些工具來(lái)協(xié)助。比如我們現(xiàn)在要研究Nhibernate,那么我們首先要熟練它的一些功能,然后從一個(gè)點(diǎn)出發(fā)慢慢編寫單元測(cè)試,然后逐漸完善代碼,最后直至完成框架的搭建,這樣會(huì)給我們帶來(lái)莫大的驅(qū)動(dòng)力和成就感。除了微軟的BCL(Base Class Library)和企業(yè)庫(kù)以外,大家還可以用TDD來(lái)試試還原以下的任一開源代碼:
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)
四. 依賴屬性續(xù)前緣
大家都知道WPF和Silverlight帶來(lái)了很多新的特性,其中一大亮點(diǎn)是引入了一種新的屬性機(jī)制——依賴屬性。依賴屬性基本應(yīng)用在了WPF的所有需要設(shè)置屬性的元素。依賴屬性根據(jù)多個(gè)提供對(duì)象來(lái)決定它的值(可以是動(dòng)畫、父類元素、綁定、樣式和模板等),同時(shí)這個(gè)值也能及時(shí)響應(yīng)變化。所以WPF擁有了依賴屬性后,代碼寫起來(lái)就比較得心應(yīng)手,功能實(shí)現(xiàn)上也變得非常容易了。如果沒有依賴屬性,我們將不得不編寫大量的代碼。依賴屬性在WPF中用得非常廣泛,具體在以下幾個(gè)方面中表現(xiàn)得尤為突出:
◆ UI的強(qiáng)大屬性體系
◆ Property value inheritance(值繼承)
◆ Metadata(強(qiáng)大的元數(shù)據(jù))
◆ 屬性變化通知,限制、驗(yàn)證
◆ Resources(資源)
◆ Data binding(數(shù)據(jù)綁定)
◆ Styles、Template(樣式、模板和風(fēng)格)
◆ 路由事件、附加事件、附加行為乃至命令
◆ Animations、3D(動(dòng)畫和3D)
◆ WPF Designer Integration(WPF設(shè)計(jì)、開發(fā)集成)
在上一篇WPF基礎(chǔ)到企業(yè)應(yīng)用系列7——深入剖析依賴屬性中,我們對(duì)依賴屬性做了較詳細(xì)的介紹,那么下面我們就簡(jiǎn)單回顧一下,其實(shí)依賴屬性的實(shí)現(xiàn)很簡(jiǎn)單,只要做以下步驟就可以實(shí)現(xiàn):
◆ 第一步: 讓所在類型繼承自 DependencyObject基類,在WPF中,我們仔細(xì)觀察框架的類圖結(jié)構(gòu),你會(huì)發(fā)現(xiàn)幾乎所有的 WPF 控件都間接繼承自DependencyObject類型。
◆ 第二步:使用 public static 聲明一個(gè) DependencyProperty的變量,該變量才是真正的依賴屬性 ,看源碼就知道這里其實(shí)用了簡(jiǎn)單的單例模式的原理進(jìn)行了封裝(構(gòu)造函數(shù)私有),只暴露Register方法給外部調(diào)用。
◆ 第三步:在靜態(tài)構(gòu)造函數(shù)中完成依賴屬性的元數(shù)據(jù)注冊(cè),并獲取對(duì)象引用,看代碼就知道是把剛才聲明的依賴屬性放入到一個(gè)類似于容器的地方,沒有講實(shí)現(xiàn)原理之前,請(qǐng)容許我先這么陳述。
◆ 第四步:在前面的三步中,我們完成了一個(gè)依賴屬性的注冊(cè),那么我們?cè)鯓硬拍軐?duì)這個(gè)依賴屬性進(jìn)行讀寫呢?答案就是提供一個(gè)依賴屬性的實(shí)例化包裝屬性,通過(guò)這個(gè)屬性來(lái)實(shí)現(xiàn)具體的讀寫操作。
根據(jù)前面的四步操作,我們就可以寫出下面的代碼:
1: public class SampleDPClass : DependencyObject
2: {
3: //聲明一個(gè)靜態(tài)只讀的DependencyProperty字段
4: public static readonly DependencyProperty SampleProperty;
5:
6: static SampleDPClass()
7: {
8: //注冊(cè)我們定義的依賴屬性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: //當(dāng)值改變時(shí),我們可以在此做一些邏輯處理
16: }
17:
18: //屬性包裝器,通過(guò)它來(lái)讀取和設(shè)置我們剛才注冊(cè)的依賴屬性
19: public string Sample
20: {
21: get { return (string)GetValue(SampleProperty); }
22: set { SetValue(SampleProperty, value); }
23: }
24: }
五. 引入測(cè)試驅(qū)動(dòng)開發(fā)
1,引入概念
由于本篇的依賴屬性體系是基于測(cè)試驅(qū)動(dòng)開發(fā)完成的,所以我們就先來(lái)看一下什么叫測(cè)試驅(qū)動(dòng)開發(fā):測(cè)試驅(qū)動(dòng)開發(fā)的基本思想就是在開發(fā)功能代碼之前,先編寫測(cè)試代碼。也就是說(shuō)在明確要開發(fā)某個(gè)功能后,首先思考如何對(duì)這個(gè)功能進(jìn)行測(cè)試,并完成測(cè)試代碼的編寫,然后編寫相關(guān)的代碼滿足這些測(cè)試用例。然后循環(huán)進(jìn)行添加其他功能,直到完全部功能的開發(fā)。由于過(guò)程很長(zhǎng),在寫的時(shí)候也省略了不少步驟,所以有些地方銜接不是那么的流暢,對(duì)此表示非常的抱歉!
2,注意事項(xiàng)
根據(jù)自身做項(xiàng)目使用TDD的一點(diǎn)微薄經(jīng)驗(yàn),總結(jié)了以下幾個(gè)注意事項(xiàng):
◆ 找準(zhǔn)切入點(diǎn):
不論是開發(fā)一個(gè)新的系統(tǒng)還是復(fù)原系統(tǒng),都必須先找準(zhǔn)一個(gè)或多個(gè)切入點(diǎn),從切入點(diǎn)經(jīng)歷”測(cè)試代碼-功能代碼-測(cè)試-重構(gòu)“來(lái)逐漸完善整個(gè)系統(tǒng),往往這個(gè)切入點(diǎn)就是功能點(diǎn),就是這個(gè)系統(tǒng)具備哪些功能,然后根據(jù)這些功能寫出測(cè)試用例。
◆ 測(cè)試列表:
大家都知道一個(gè)系統(tǒng)或者一個(gè)框架都是很龐大的,如果要引入測(cè)試驅(qū)動(dòng)開發(fā),首先我們必須要有一個(gè)測(cè)試列表,在任何階段想添加功能需求問題時(shí),把相關(guān)功能點(diǎn)加到測(cè)試列表中,然后繼續(xù)開發(fā)的工作。然后不斷的完成對(duì)應(yīng)的測(cè)試用例、功能代碼、重構(gòu)。這樣可以避免疏漏的同時(shí)也能把控當(dāng)前的進(jìn)度。
◆ 測(cè)試驅(qū)動(dòng):
這個(gè)比較核心。完成某個(gè)功能,某個(gè)類,首先編寫測(cè)試代碼,考慮其如何使用、如何測(cè)試。然后在對(duì)其進(jìn)行設(shè)計(jì)、編碼。這里也強(qiáng)調(diào)先編寫對(duì)功能代碼的判斷用的斷言語(yǔ)句,然后編寫相應(yīng)的輔助語(yǔ)句。
◆ 良好的代碼設(shè)計(jì)及可測(cè)性:
功能代碼設(shè)計(jì)、開發(fā)時(shí)應(yīng)該具有較強(qiáng)的可測(cè)試性。應(yīng)該盡量保持良好的設(shè)計(jì)原則和代碼規(guī)范,如盡量依賴于接口、盡量高內(nèi)聚、低耦合等等。
◆ 模塊或功能隔離:
不同代碼的測(cè)試應(yīng)該相互隔離。對(duì)一塊代碼的測(cè)試只考慮此代碼的測(cè)試,不要考慮其實(shí)現(xiàn)細(xì)節(jié),不然就會(huì)陷入一團(tuán)亂麻之中,這個(gè)可以通過(guò)MOCK來(lái)實(shí)現(xiàn),同時(shí)在開始的時(shí)候也要?jiǎng)澐趾眠吔纭?/p>
◆ 適當(dāng)引入MOCK:
在適當(dāng)情況下引入MOCK來(lái)完成單元測(cè)試,這種情況尤其是在邊際交互比較多的案例當(dāng)中,對(duì)于交互比較多且復(fù)雜的多個(gè)類關(guān)系可以用MOCK暫時(shí)模擬,這是一個(gè)不錯(cuò)的解決方案。
◆ 由小到大、由偏到全、統(tǒng)籌兼顧:
一個(gè)產(chǎn)品或者一個(gè)項(xiàng)目是比較大的,所以我們這里就需要遵循由小到大、由偏到全、統(tǒng)籌兼顧的原則,分解功能和代碼。把所有的規(guī)模大、復(fù)雜性高的工作,分解成小的任務(wù)來(lái)完成,這樣既方便團(tuán)隊(duì)協(xié)作,同時(shí)也減輕了復(fù)雜度,使整個(gè)開發(fā)一下子變得簡(jiǎn)單了許多。
◆ 保持隨時(shí)重構(gòu)的習(xí)慣:
很多開發(fā)者在經(jīng)過(guò)測(cè)試代碼-功能代碼-測(cè)試通過(guò)以后就當(dāng)完成了任務(wù),其實(shí)你會(huì)發(fā)現(xiàn)隨著其他功能的引入或者使用過(guò)程中發(fā)現(xiàn)了很多重復(fù)、冗余的代碼、再或者先前的代碼結(jié)構(gòu)和設(shè)計(jì)不太合理,這個(gè)時(shí)候就需要隨時(shí)的進(jìn)行重構(gòu)和單元測(cè)試,在一方面可以避免產(chǎn)生風(fēng)險(xiǎn),另一方面可以使系統(tǒng)更加完善。
◆ 隨時(shí)進(jìn)行回歸:
在”測(cè)試代碼-功能代碼-測(cè)試-重構(gòu)“的循環(huán)中一定要記住多回歸,因?yàn)檫@樣可以保證當(dāng)前的代碼是不是會(huì)影響到前面的功能,其實(shí)只需要看看紅綠燈就行。
◆ 查看和統(tǒng)計(jì)代碼覆蓋率:
通過(guò)前面的步驟之后,我們就要看一下實(shí)現(xiàn)的功能是否達(dá)到我們的預(yù)期目標(biāo),除了功能完善之外,還要保證代碼的覆蓋率,因?yàn)樗且粋€(gè)系統(tǒng)穩(wěn)定與否、可維護(hù)性與否的一個(gè)重大標(biāo)志。
3,工具介入
以后寫關(guān)于TDD的文章可能比較多,同時(shí)也都會(huì)用到這個(gè)工具,所以我們今天對(duì)它也稍帶介紹一下,正所謂“工欲善其事,必先利其器”。根據(jù)官方文檔解釋:TestDriven.NET是Visual Studio的一個(gè)TDD插件,最近版本是TestDriven.NET-3.0.2749 RTM版。其中一些新特性有:支持MSTest、.NET Reflector 6 Pro、VS 2010、Silverlight 4、NUnit 2.5.3,使用項(xiàng)目所用的.NET框架等。 下載地址:http://www.testdriven.NET/
這個(gè)工具使用起來(lái)比VS自帶的單元測(cè)試和測(cè)試覆蓋功能好用,所以從2008年開始基本就用它作為一個(gè)必備的工具使用。關(guān)于它具體的功能和怎么使用,我們這里不詳細(xì)介紹,網(wǎng)上也有很多文章,大家可以做一下參考和研究。下圖是安裝后以插件的形式出現(xiàn)在VS中的效果:
A,基本介紹
TestDriven.NET原來(lái)叫做NUnitAddIn,它是個(gè)Visual Studio插件,集成了如下測(cè)試框架:NUnit、MbUnit、 ZaneBug、MSTest、NCover、NCoverExplorer、Reflector、TypeMock、dotTrace和MSBee,它主要面向使用TDD的開發(fā)者,主要特性列舉如下:
◆ 單鍵運(yùn)行方法、類、命名空間、項(xiàng)目和解決方案中的單元測(cè)試
◆ 能夠快速測(cè)試實(shí)例方法、靜態(tài)方法或?qū)傩?/p>
◆ 可以直接跳到.NET Reflector中的任何方法、類型、項(xiàng)目或引用中,這個(gè)功能提供了相當(dāng)大的方便
◆ 在調(diào)試過(guò)程中可以查看.NET Reflector中的任何模塊或堆棧信息
◆ 支持多種單元測(cè)試框架,包括NUnit、MbUnit、xUnit和MSTest
◆ 測(cè)試運(yùn)行在自己的進(jìn)程中以消除其他問題和邊際效應(yīng)
◆ 可以輕松對(duì)任何目標(biāo)測(cè)試進(jìn)行調(diào)試或執(zhí)行代碼覆蓋率測(cè)試(比微軟自帶的單元測(cè)試和代碼覆蓋功能要好用多了)
◆ 支持所有主流的.NET語(yǔ)言:C#、VB、C++和F#
B,TestDriven.NET 3.0中的新特性:
◆ TestDriven.NET是基于.NET框架的。再由于VS 2010支持使用多個(gè).NET版本,所以支持各個(gè)VS版本和工具就沒有問題了
◆ 完全支持在VS 2008和VS 2010中使用MSTest
◆ 完全支持.NET Reflector 6 Pro
◆ 支持NUnit 2.5.3
◆ 支持和兼容VS 2005、VS 2008、VS 2010幾個(gè)版本
◆ 支持Silverlight 4的測(cè)試
C,兼容性
TestDriven.NET兼容于如下VS版本:Windows XP、Vista、Windows 7、Windows 2000、Windows 2003和Windows 2008(32和64位)上的Visual Studio 2005、2008和2010。官方已經(jīng)不再對(duì)VS 2003支持。
D,版本
◆ 企業(yè)版:每臺(tái)機(jī)器一個(gè)許可認(rèn)證
◆ 專業(yè)版:一般的許可形式
◆ 個(gè)人版:面向?qū)W生、開源開發(fā)者和試驗(yàn)用戶的免費(fèi)許可(大家可以下載這個(gè)版本,個(gè)人感覺很好用)
4,關(guān)于本篇
本篇文章沒有明確的寫作意圖,只是最近在深入研究MONO源碼時(shí)有感而發(fā),當(dāng)然作者本人也只是起到了一個(gè)研究者或者剖析者的角色。首先實(shí)現(xiàn)最簡(jiǎn)單且基本的DependencyProperty.Register功能,然后再實(shí)現(xiàn)DependencyObject的GetValue和SetValue,接著實(shí)現(xiàn)PropertyMetadata的DefaultValue、PropertyChangedCallback、CoerceValueCallback等功能,然后完善DependencyProperty.Register注冊(cè)時(shí)添加ValidateValueCallback、RegisterAttached、RegisterAttachedReadOnly、RegisterReadOnly、OverrideMetadata、GetMetadata和AddOwner等相關(guān)功能。既然有了這些功能,自然就需要完善PropertyMetadata的IsSealed、Merge和OnApply等相關(guān)底層操作。當(dāng)然在中間還需要DependencyObject的ClearValue、CoerceValue、GetLocalValueEnumerator、ReadLocalValue以及其他的Helper類,這里就不一一進(jìn)行說(shuō)明。對(duì)于邊際交互比較多且關(guān)聯(lián)比較大的操作,采用了Mock進(jìn)行暫時(shí)模擬,在開發(fā)完了以后再進(jìn)行了替換。在開發(fā)過(guò)程中,隨時(shí)進(jìn)行單元測(cè)試和覆蓋率的檢查,這樣可以方便查看哪些功能還有問題以及整體的進(jìn)度和質(zhì)量的監(jiān)控。
六. DependencyProperty測(cè)試代碼
在寫DependencyProperty測(cè)試代碼之前,我們先看一下它到底有哪些成員和方法,如下圖:
了解了上面DependencyProperty的基本功能,我們首先創(chuàng)建一個(gè)繼承自DependencyObject的類ObjectPoker,由于DependencyObject還沒有被創(chuàng)建,所以我們這里就先創(chuàng)建它,然后在ObjectPoker類里面實(shí)現(xiàn)我們的經(jīng)典語(yǔ)句DependencyProperty.Register,由于Register有很多重載,為了方便TDD,就從最簡(jiǎn)單的開始(三個(gè)參數(shù),不牽涉到元數(shù)據(jù)類),然后再創(chuàng)建一個(gè)ObjectPoker的子類,這是方便后面測(cè)試DependencyProperty的相關(guān)功能。
1: class ObjectPoker : DependencyObject
2: {
3: //注冊(cè)依賴屬性property1
4: public static readonly DependencyProperty TestProp1 = DependencyProperty.Register("property1", typeof(string), typeof(ObjectPoker));
5: }
6:
7: class SubclassPoker : ObjectPoker
8: {
9: }
九. DependencyObject實(shí)現(xiàn)代碼
通過(guò)前面的測(cè)試用例,DependencyObject類的基本功能已經(jīng)完成,不過(guò)我們要注意幾個(gè)要點(diǎn):
1,依賴屬性其實(shí)終究要DependencyObject和DependencyProperty成對(duì)才能算得上真正的DependencyProperty。
2,不管是Register、RegisterAttached、RegisterAttachedReadOnly還是RegisterReadOnly操作,我們都要通過(guò)DependencyObject來(lái)操作DependencyProperty的值,也就是通過(guò)DependencyObject這個(gè)外部接口來(lái)操作,DependencyProperty只負(fù)責(zé)注冊(cè)和內(nèi)部處理,不負(fù)責(zé)外部接口。
3,在DependencyObject中提供了幾個(gè)操作LocalValue的接口的接口,其中包括ReadLocalValue、GetLocalValueEnumerator、CoerceValue和ClearValue等。
4,在注冊(cè)注冊(cè)依賴屬性時(shí),實(shí)質(zhì)是關(guān)聯(lián)DependencyObject的propertyDeclarations,它是一個(gè)Dictionary<Type,Dictionary<string,DependencyProperty>>類型,但是在register代碼中并沒有完全關(guān)聯(lián)起來(lái),我也比較納悶,所以這點(diǎn)還希望和大家一起探討,微軟的BCL并沒有這么實(shí)現(xiàn)。
1: using System.Collections.Generic;
2: //using System.Windows.Threading;
3:
4: namespace System.Windows
5: {
6: public class DependencyObject
7: {
8: //依賴屬性其實(shí)終究要DependencyObject和DependencyProperty成對(duì)才能算得上真正的DependencyProperty
9: private static Dictionary<Type,Dictionary<string,DependencyProperty>> propertyDeclarations = new Dictionary<Type,Dictionary<string,DependencyProperty>>();
10: //該依賴屬性的鍵值對(duì),鍵為DependencyProperty,值為object
11: private Dictionary<DependencyProperty,object> properties = new Dictionary<DependencyProperty,object>();
12:
13: //是否已密封,沒有實(shí)現(xiàn)DependencyObject層次的IsSealed判斷
14: public bool IsSealed {
15: get { return false; }
16: }
17:
18: //獲取該DependencyObject的DependencyObjectType
19: public DependencyObjectType DependencyObjectType {
20: get { return DependencyObjectType.FromSystemType (GetType()); }
21: }
22:
23: //根據(jù)該依賴屬性名,清除它的值
24: public void ClearValue(DependencyProperty dp)
25: {
26: if (IsSealed)
27: throw new InvalidOperationException ("Cannot manipulate property values on a sealed DependencyObject");
28:
29: properties[dp] = null;
30: }
31:
32: //根據(jù)該依賴屬性DependencyPropertyKey,清除它的值
33: public void ClearValue(DependencyPropertyKey key)
34: {
35: ClearValue (key.DependencyProperty);
36: }
37:
38: //根據(jù)該依賴屬性名,強(qiáng)制值
39: public void CoerceValue (DependencyProperty dp)
40: {
41: PropertyMetadata pm = dp.GetMetadata (this);
42: if (pm.CoerceValueCallback != null)
43: pm.CoerceValueCallback (this, GetValue (dp));
44: }
45:
46: public sealed override bool Equals (object obj)
47: {
48: throw new NotImplementedException("Equals");
49: }
50:
51: public sealed override int GetHashCode ()
52: {
53: throw new NotImplementedException("GetHashCode");
54: }
55:
56: //得到本地值的枚舉器
57: public LocalValueEnumerator GetLocalValueEnumerator()
58: {
59: return new LocalValueEnumerator(properties);
60: }
61:
62: //根據(jù)依賴屬性名獲取值
63: public object GetValue(DependencyProperty dp)
64: {
65: object val = properties[dp];
66: return val == null ? dp.DefaultMetadata.DefaultValue : val;
67: }
68:
69:
70: public void InvalidateProperty(DependencyProperty dp)
71: {
72: throw new NotImplementedException("InvalidateProperty(DependencyProperty dp)");
73: }
74:
75: //當(dāng)屬性值改變時(shí),觸發(fā)回調(diào)
76: protected virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
77: {
78: PropertyMetadata pm = e.Property.GetMetadata (this);
79: if (pm.PropertyChangedCallback != null)
80: pm.PropertyChangedCallback (this, e);
81: }
82:
83: //提供一個(gè)外界查看LocalValue的接口
84: public object ReadLocalValue(DependencyProperty dp)
85: {
86: object val = properties[dp];
87: return val == null ? DependencyProperty.UnsetValue : val;
88: }
89:
90: //根據(jù)依賴屬性名設(shè)置其值
91: public void SetValue(DependencyProperty dp, object value)
92: {
93: if (IsSealed)
94: throw new InvalidOperationException ("Cannot manipulate property values on a sealed DependencyObject");
95:
96: if (!dp.IsValidType (value))
97: throw new ArgumentException ("value not of the correct type for this DependencyProperty");
98:
99: ValidateValueCallback validate = dp.ValidateValueCallback;
100: if (validate != null && !validate(value))
101: throw new Exception("Value does not validate");
102: else
103: properties[dp] = value;
104: }
105:
106: //根據(jù)依賴屬性DependencyPropertyKey設(shè)置其值
107: public void SetValue(DependencyPropertyKey key, object value)
108: {
109: SetValue (key.DependencyProperty, value);
110: }
111:
112: protected virtual bool ShouldSerializeProperty (DependencyProperty dp)
113: {
114: throw new NotImplementedException ();
115: }
116:
117: //這里的注冊(cè)實(shí)質(zhì)是關(guān)聯(lián)propertyDeclarations
118: internal static void register(Type t, DependencyProperty dp)
119: {
120: if (!propertyDeclarations.ContainsKey (t))
121: propertyDeclarations[t] = new Dictionary<string,DependencyProperty>();
122: Dictionary<string,DependencyProperty> typeDeclarations = propertyDeclarations[t];
123: if (!typeDeclarations.ContainsKey(dp.Name))
124: {
125: typeDeclarations[dp.Name] = dp;
126: //這里仍然有一些問題,期待各位共同探討解決
127: }
128: else
129: throw new ArgumentException("A property named " + dp.Name + " already exists on " + t.Name);
130: }
131: }
132: }
NET技術(shù):依賴屬性之“風(fēng)云再起”,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。