国产自产第一-国产自产对白一区-国产自产精品-国产自产区44页-国产自产在线-国产自产自拍-国产自产自拍视频-国产自精品

金喜正规买球

WPF基礎(chǔ)到企業(yè)應(yīng)用系列8——依賴屬性之“風(fēng)云再起”

轉(zhuǎn)帖|其它|編輯:郝浩|2010-11-04 15:46:03.000|閱讀 957 次

概述:在上一篇WPF基礎(chǔ)到企業(yè)應(yīng)用系列7——深入剖析依賴屬性中,我們首先從依賴屬性基本介紹講起,然后過渡到依賴屬性的優(yōu)先級、附加屬性、只讀依賴屬性、依賴屬性元數(shù)據(jù)、依賴屬性回調(diào)、驗證及強制值、依賴屬性監(jiān)聽、代碼段(自動生成) 等相關(guān)知識,最后我們模擬一個WPF依賴屬性的實現(xiàn),由于上篇是根據(jù)微軟WPF的BCL源碼剖析的,所以這篇我們就研究一下.NET的跨平臺版本MONO,看下它是怎么來實現(xiàn)這個依賴屬性機制。

# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>

一. 摘要

  首先圣殿騎士很高興”WPF 基礎(chǔ)到企業(yè)應(yīng)用系列” 能得到大家的關(guān)注、支持和認可。看到很多朋友留言希望加快速度的問題,我會盡力的,對你們的熱情關(guān)注也表示由衷的感謝。這段時間更新慢的主要原因是因為忙著用TDD還原MONO的框架,同時也因為一直在研究云計算,所以就拖拖拉拉一直沒有發(fā)布后面的文章。由于WPF整個系列是自己的一些粗淺心得和微薄經(jīng)驗,所以不會像寫書那么面面俱到,如果有不足或者錯誤之處也請大家見諒。在今年之內(nèi)圣殿騎士會盡量完成”WPF 基礎(chǔ)到企業(yè)應(yīng)用系列”和”云計算之旅系列“,誠然,由于本人才識淺薄,所以熱切希望和大家共勉!

  由于依賴屬性是WPF和Silverlight的核心概念,微軟在C\S和B\S平臺上主要精力都放到了WPF和Silverlight技術(shù)上,同時Silverlight也是Windows Phone的兩大編程模型之一(另外一種是XNA),所以我們花費了大量的時間和篇幅進行論述。在上一篇WPF基礎(chǔ)到企業(yè)應(yīng)用系列7——深入剖析依賴屬性中,我們首先從依賴屬性基本介紹講起,然后過渡到依賴屬性的優(yōu)先級、附加屬性、只讀依賴屬性、依賴屬性元數(shù)據(jù)、依賴屬性回調(diào)、驗證及強制值、依賴屬性監(jiān)聽、代碼段(自動生成) 等相關(guān)知識,最后我們模擬一個WPF依賴屬性的實現(xiàn),由于上篇是根據(jù)微軟WPF的BCL源碼剖析的,所以這篇我們就研究一下.NET的跨平臺版本MONO,看下它是怎么來實現(xiàn)這個依賴屬性機制。

二. 本文提綱

· 1.摘要

· 2.本文提綱

· 3.兵馬未動、廢話先行

· 4.依賴屬性續(xù)前緣

· 5.引入測試驅(qū)動開發(fā)

· 6.DependencyProperty測試代碼

· 7.DependencyProperty實現(xiàn)代碼

· 8.DependencyObject測試代碼

· 9.DependencyObject實現(xiàn)代碼

· 10.PropertyMetadata測試代碼

· 11.PropertyMetadata實現(xiàn)代碼

· 12.其他協(xié)助類測試代碼

· 13.其他協(xié)助類的實現(xiàn)代碼

· 14.回歸并統(tǒng)計覆蓋率

· 15.簡單驗證依賴屬性系統(tǒng)

· 16.本文總結(jié)

· 17.相關(guān)代碼下載

· 18.系列進度

三. 兵馬未動,廢話先行

在講這篇文章之前,我們先來拉一拉家常,說點題外話,就當(dāng)進入正餐之前的一些甜點,當(dāng)然這里主要針對.NET平臺而言:

1,淺談軟件技術(shù)的發(fā)展趨勢及定位

  互聯(lián)網(wǎng)的普及應(yīng)用催生了很多技術(shù)的發(fā)展與更新,如果仔細深究,你會發(fā)現(xiàn)軟件技術(shù)的發(fā)展趨勢將主要體現(xiàn)在以下四個方面:客戶端軟件開發(fā)(其中包括客戶端軟件、游戲、中間件和嵌入式開發(fā)等)、Web 開發(fā)(包括傳統(tǒng)的Web技術(shù)、Web游戲以及一些在線應(yīng)用)、移動設(shè)備軟件開發(fā)(主要涉及到手機等移動設(shè)備)、云計算開發(fā)(公有云、私有云、混合云會逐漸界限清晰,云廠商以及云平臺也會逐漸整合和成熟起來)。就微軟來說,這四個方面主要如下:

客戶端軟件開發(fā)

  目前微軟主要有Win32 應(yīng)用程序、MFC 應(yīng)用程序、WinForm應(yīng)用程序和WPF 應(yīng)用程序作為開發(fā)選擇,目前這四種技術(shù)還會共存,因為不同的需求以及不同的人群都有不同的需要。當(dāng)然WPF借助于其強大的功能和迅猛的發(fā)展速度很快會成為首選,這個是值得肯定的。

Web 開發(fā)

  在WEB方面微軟主要有ASP.NET、ASP.NET MVC、Silverlight三種技術(shù),ASP.NET技術(shù)已經(jīng)發(fā)展了多年,在未來的很長一段時間內(nèi)還會是主流,同時結(jié)合Silverlight作為局部和整體應(yīng)用效果都還很不錯,所以這也是很多企業(yè)的首選。ASP.NET MVC在目前來說應(yīng)用還不是特別廣泛,不過用過之后感覺也還不錯,只是還需要一段時間的適應(yīng)過程而已。Silverlight在構(gòu)建局部應(yīng)用和整站應(yīng)用都發(fā)揮了不錯的優(yōu)勢,在Windows Phone中也表現(xiàn)得不錯,所以這個技術(shù)將會一直熱下去。

移動設(shè)備軟件開發(fā)

  移動設(shè)備方面可謂是現(xiàn)在眾廠商競爭最激烈的市場之一,也是傳統(tǒng)技術(shù)和新型技術(shù)的主要戰(zhàn)場之一。微軟現(xiàn)在主推的Windows Phone開發(fā)主要包括Silverlight和XNA兩種技術(shù),Windows Phone開發(fā)逐漸變得和ASP.NET開發(fā)一樣簡單,這也是微軟的一個目標(biāo)。

云計算開發(fā)

  云計算現(xiàn)在基本上成了互聯(lián)網(wǎng)的第一大熱門詞,不管是軟件為主導(dǎo)的企業(yè),還是以硬件為主導(dǎo)的企業(yè),都卷入了這場紛爭與革命。微軟的云平臺——Windows Azure Platform,它是微軟完整的云計算平臺,目前包含了如下三大部分(Windows Azure:運行在云中的操作系統(tǒng),對于用戶來說是虛擬且透明的,其中提供了Compute(計算),Storage(存儲),以及Manage(管理)這三個主要功能及其底層服務(wù),使用起來相當(dāng)?shù)谋憬荨QL Azure:運行于云中的一個關(guān)系數(shù)據(jù)庫,和SQL Server 2008類似,但是在功能上還沒有那么強大。AppFabric:全名是Windows Azure platform AppFabric,提供了訪問控制、服務(wù)總線等服務(wù),主要用于把基礎(chǔ)應(yīng)用連接到云中)。

其實把這四個方面總結(jié)起來就是傳說中的微軟“三屏一云”戰(zhàn)略,從中也可以看出微軟逍遙于天地,縱橫于宇內(nèi),嘯傲于世間,雄霸于大地的梟雄戰(zhàn)略!

2,淺談微軟跨平臺與MONO

  在談之前我們先看一下什么是MONO?MONO項目是由Ximian發(fā)起、Miguel de lcaza領(lǐng)導(dǎo)、Novell公司主持的項目。它是一個致力于開創(chuàng).NET在Linux,F(xiàn)reeBSD,Unix,Mac OS X和Solaris等其他平臺使用的開源工程。它包含了一個C#語言的編譯器,一個CLR的運行時,和一組類庫,并逐漸實現(xiàn)了 ADO.NET、ASP.NET、WinForm、Silverlight(可惜沒有實現(xiàn)強大的WPF),能夠使得開發(fā)人員在其他平臺用C#開發(fā)程序。

值得看好的地方:

1,跨平臺:開創(chuàng).NET在Linux,F(xiàn)reeBSD,Unix,Mac OS X和Solaris等其他平臺使用,這是微軟沒有實現(xiàn)的,但是MONO進行了補充,所以值得看好。

2,開源:不論使用什么技術(shù),大家似乎都希望能夠用開源的產(chǎn)品,一方面是考慮到技術(shù)的可控性和可維護性;另一方面則是考慮到安全性,當(dāng)然在另一個角度也是可以學(xué)習(xí)到其中的一些技術(shù)和思想,所以大家對開源總是報以歡迎的態(tài)度。

3,不同的方式實現(xiàn).NET框架:由于微軟對技術(shù)申請了專利,所以MONO不能盲目的模仿,對很多細節(jié)都改用自己的方式進行了實現(xiàn),所以我們也可以學(xué)到很多不一樣的實現(xiàn)方式。

4,持續(xù)更新:MONO從一開始到現(xiàn)在始終在更新,其中包括bug修復(fù)、版本升級、增加新的功能及應(yīng)用,所以相信它會在不斷的更新中更加完善。

不足之處:

1.模仿但要避免專利:由于是模仿微軟.NET平臺,但因為微軟對代碼申請了專利,所以MONO只能采用其它實現(xiàn)方式來實現(xiàn)同樣的功能,這樣一來很多地方就會實現(xiàn)得很累贅,效率也會受損。

2.沒有擺脫實驗產(chǎn)品的頭銜:由于它目前的使用比較低,所以信息反饋和持續(xù)改進就做得比較弱,這也是目前功能完善得比較慢的原因之一吧。

3,功能還需要完善:一些主要功能還未實現(xiàn),如作為Windows平臺最基礎(chǔ)的COM和COM+功能沒有保存,像MSMQ等消息隊列,消息傳送的功能也沒有實現(xiàn),對ADO.NET、XML等核心功能效率有待提升,對BCL庫代碼也有很多需要優(yōu)化的地方,強大的WPF也沒有引入。

4.效率和用戶體驗還有待提升。

與微軟之間的關(guān)系

  微軟與MONO之間的關(guān)系也一直處于不冷不熱的狀態(tài),沒有明確的反對,也沒有明確的支持,究其原因筆者認為主要有以下兩點:

1,微軟帶來最大收益的產(chǎn)品仍舊是Windows操作系統(tǒng)和Office等軟件,微軟在其他領(lǐng)域盈利都沒有這兩大產(chǎn)品來得直接。而.NET作為微軟的強大開發(fā)平臺,是不希望落在其他平臺上運行的,這樣就會削弱Windows操作系統(tǒng)和Office等軟件的市場占有率,所以讓.NET跨平臺對微軟來說是一件舍本求末的事情,這也是微軟不主張.NET運行于其他平臺的主要原因,你想微軟是一個以技術(shù)為主導(dǎo)的公司,任何IT市場都會有它的身影,如果想讓.NET跨平臺,那豈不是一件很輕而易舉的事情嗎?

2,由于MONO還沒有成熟,在很多方面都表現(xiàn)得像一個實驗室產(chǎn)品,在根本上沒有對微軟構(gòu)成威脅,況且在外界質(zhì)疑.NET是否能跨平臺的時候,還有一個現(xiàn)身的說法,所以微軟也不會明確的反對和支持。

總結(jié)

  雖然目前來說MONO喜憂參半,但優(yōu)點始終要大于缺點,畢竟每一個框架或者產(chǎn)品都是慢慢不斷改進而完善的,更何況開源必將是未來的一個趨勢,所以我們有理由也有信心期待它接下來的發(fā)展。

3,談?wù)勗创a研究與TDD

  大家都有一個共識:如果你想研究某個框架或者工具的源碼,那先必須熟練使用它,熟練之后自然就有一種研究它的沖動,但是往往這個框架或工具比較龐大,很不容易下手,一個很不錯的方法就是使用TDD。我們都知道TDD的基本思想就是在開發(fā)功能代碼之前,先編寫測試代碼。也就是說在明確要開發(fā)某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關(guān)的代碼滿足這些測試用例。然后循環(huán)進行添加其他功能,直到完全部功能的開發(fā),在此過程中我們可以借助一些工具來協(xié)助。比如我們現(xiàn)在要研究Nhibernate,那么我們首先要熟練它的一些功能,然后從一個點出發(fā)慢慢編寫單元測試,然后逐漸完善代碼,最后直至完成框架的搭建,這樣會給我們帶來莫大的驅(qū)動力和成就感。除了微軟的BCL(Base Class Library)和企業(yè)庫以外,大家還可以用TDD來試試還原以下的任一開源代碼:

Spring.NET()、Castle()、log4net()、

NHibernate()、iBATIS.NET()、Caliburn()、

MVVM Light Toolkit()、Prism()、MONO源碼()

四. 依賴屬性續(xù)前緣

  大家都知道WPF和Silverlight帶來了很多新的特性,其中一大亮點是引入了一種新的屬性機制——依賴屬性。依賴屬性基本應(yīng)用在了WPF的所有需要設(shè)置屬性的元素。依賴屬性根據(jù)多個提供對象來決定它的值(可以是動畫、父類元素、綁定、樣式和模板等),同時這個值也能及時響應(yīng)變化。所以WPF擁有了依賴屬性后,代碼寫起來就比較得心應(yīng)手,功能實現(xiàn)上也變得非常容易了。如果沒有依賴屬性,我們將不得不編寫大量的代碼。依賴屬性在WPF中用得非常廣泛,具體在以下幾個方面中表現(xiàn)得尤為突出:

UI的強大屬性體系

Property value inheritance(值繼承)

Metadata(強大的元數(shù)據(jù))

屬性變化通知,限制、驗證

Resources(資源)

Data binding(數(shù)據(jù)綁定)

Styles、Template(樣式、模板和風(fēng)格)

路由事件、附加事件、附加行為乃至命令

Animations、3D(動畫和3D)

WPF Designer Integration(WPF設(shè)計、開發(fā)集成)

  在上一篇WPF基礎(chǔ)到企業(yè)應(yīng)用系列7——深入剖析依賴屬性中,我們對依賴屬性做了較詳細的介紹,那么下面我們就簡單回顧一下,其實依賴屬性的實現(xiàn)很簡單,只要做以下步驟就可以實現(xiàn):

第一步: 讓所在類型繼承自 DependencyObject基類,在WPF中,我們仔細觀察框架的類圖結(jié)構(gòu),你會發(fā)現(xiàn)幾乎所有的 WPF 控件都間接繼承自DependencyObject類型。
第二步:使用 public static 聲明一個 DependencyProperty的變量,該變量才是真正的依賴屬性 ,看源碼就知道這里其實用了簡單的單例模式的原理進行了封裝(構(gòu)造函數(shù)私有),只暴露Register方法給外部調(diào)用。
第三步:在靜態(tài)構(gòu)造函數(shù)中完成依賴屬性的元數(shù)據(jù)注冊,并獲取對象引用,看代碼就知道是把剛才聲明的依賴屬性放入到一個類似于容器的地方,沒有講實現(xiàn)原理之前,請容許我先這么陳述。
第四步:在前面的三步中,我們完成了一個依賴屬性的注冊,那么我們怎樣才能對這個依賴屬性進行讀寫呢?答案就是提供一個依賴屬性的實例化包裝屬性,通過這個屬性來實現(xiàn)具體的讀寫操作。

根據(jù)前面的四步操作,我們就可以寫出下面的代碼:

public class SampleDPClass : DependencyObject
{
//聲明一個靜態(tài)只讀的DependencyProperty字段
public static readonly DependencyProperty SampleProperty;


static SampleDPClass()
{
//注冊我們定義的依賴屬性Sample
SampleProperty = DependencyProperty.Register("Sample", typeof(string), typeof(SampleDPClass),
new PropertyMetadata("Knights Warrior!", OnValueChanged));
}

private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
//當(dāng)值改變時,我們可以在此做一些邏輯處理
}

//屬性包裝器,通過它來讀取和設(shè)置我們剛才注冊的依賴屬性
public string Sample
{
get { return (string)GetValue(SampleProperty); }
set { SetValue(SampleProperty, value); }
}
}

  通過上面的例子可以看出,我們一般.NET屬性是直接對類的一個私有屬性進行封裝,所以讀取值的時候,也就是直接讀取這個字段;而依賴屬性則是通過調(diào)用繼承自DependencyObject的GetValue()和SetValue來進行操作,它實際存儲在DependencyProperty的一個IDictionary的鍵-值配對字典中,所以一條記錄中的鍵(Key)就是該屬性的HashCode值,而值(Value)則是我們注冊的DependencyProperty。 回顧了一些基礎(chǔ)知識,那我們下面就開始今天的依賴屬性系統(tǒng)TDD之旅。

五. 引入測試驅(qū)動開發(fā) 1,引入概念

  由于本篇的依賴屬性體系是基于測試驅(qū)動開發(fā)完成的,所以我們就先來看一下什么叫測試驅(qū)動開發(fā):測試驅(qū)動開發(fā)的基本思想就是在開發(fā)功能代碼之前,先編寫測試代碼。也就是說在明確要開發(fā)某個功能后,首先思考如何對這個功能進行測試,并完成測試代碼的編寫,然后編寫相關(guān)的代碼滿足這些測試用例。然后循環(huán)進行添加其他功能,直到完全部功能的開發(fā)。由于過程很長,在寫的時候也省略了不少步驟,所以有些地方銜接不是那么的流暢,對此表示非常的抱歉!

2,注意事項

根據(jù)自身做項目使用TDD的一點微薄經(jīng)驗,總結(jié)了以下幾個注意事項:

◆ 找準(zhǔn)切入點:

  不論是開發(fā)一個新的系統(tǒng)還是復(fù)原系統(tǒng),都必須先找準(zhǔn)一個或多個切入點,從切入點經(jīng)歷”測試代碼-功能代碼-測試-重構(gòu)“來逐漸完善整個系統(tǒng),往往這個切入點就是功能點,就是這個系統(tǒng)具備哪些功能,然后根據(jù)這些功能寫出測試用例。

◆ 測試列表:

  大家都知道一個系統(tǒng)或者一個框架都是很龐大的,如果要引入測試驅(qū)動開發(fā),首先我們必須要有一個測試列表,在任何階段想添加功能需求問題時,把相關(guān)功能點加到測試列表中,然后繼續(xù)開發(fā)的工作。然后不斷的完成對應(yīng)的測試用例、功能代碼、重構(gòu)。這樣可以避免疏漏的同時也能把控當(dāng)前的進度。

◆ 測試驅(qū)動:

  這個比較核心。完成某個功能,某個類,首先編寫測試代碼,考慮其如何使用、如何測試。然后在對其進行設(shè)計、編碼。這里也強調(diào)先編寫對功能代碼的判斷用的斷言語句,然后編寫相應(yīng)的輔助語句。

◆ 良好的代碼設(shè)計及可測性:

功能代碼設(shè)計、開發(fā)時應(yīng)該具有較強的可測試性。應(yīng)該盡量保持良好的設(shè)計原則和代碼規(guī)范,如盡量依賴于接口、盡量高內(nèi)聚、低耦合等等。

◆ 模塊或功能隔離:

  不同代碼的測試應(yīng)該相互隔離。對一塊代碼的測試只考慮此代碼的測試,不要考慮其實現(xiàn)細節(jié),不然就會陷入一團亂麻之中,這個可以通過MOCK來實現(xiàn),同時在開始的時候也要劃分好邊界。

◆ 適當(dāng)引入MOCK:

  在適當(dāng)情況下引入MOCK來完成單元測試,這種情況尤其是在邊際交互比較多的案例當(dāng)中,對于交互比較多且復(fù)雜的多個類關(guān)系可以用MOCK暫時模擬,這是一個不錯的解決方案。

◆ 由小到大、由偏到全、統(tǒng)籌兼顧:

  一個產(chǎn)品或者一個項目是比較大的,所以我們這里就需要遵循由小到大、由偏到全、統(tǒng)籌兼顧的原則,分解功能和代碼。把所有的規(guī)模大、復(fù)雜性高的工作,分解成小的任務(wù)來完成,這樣既方便團隊協(xié)作,同時也減輕了復(fù)雜度,使整個開發(fā)一下子變得簡單了許多。

◆ 保持隨時重構(gòu)的習(xí)慣

  很多開發(fā)者在經(jīng)過測試代碼-功能代碼-測試通過以后就當(dāng)完成了任務(wù),其實你會發(fā)現(xiàn)隨著其他功能的引入或者使用過程中發(fā)現(xiàn)了很多重復(fù)、冗余的代碼、再或者先前的代碼結(jié)構(gòu)和設(shè)計不太合理,這個時候就需要隨時的進行重構(gòu)和單元測試,在一方面可以避免產(chǎn)生風(fēng)險,另一方面可以使系統(tǒng)更加完善。

◆ 隨時進行回歸:

  在”測試代碼-功能代碼-測試-重構(gòu)“的循環(huán)中一定要記住多回歸,因為這樣可以保證當(dāng)前的代碼是不是會影響到前面的功能,其實只需要看看紅綠燈就行。

查看和統(tǒng)計代碼覆蓋率

  通過前面的步驟之后,我們就要看一下實現(xiàn)的功能是否達到我們的預(yù)期目標(biāo),除了功能完善之外,還要保證代碼的覆蓋率,因為它是一個系統(tǒng)穩(wěn)定與否、可維護性與否的一個重大標(biāo)志。

3,工具介入

  以后寫關(guān)于TDD的文章可能比較多,同時也都會用到這個工具,所以我們今天對它也稍帶介紹一下,正所謂“工欲善其事,必先利其器”。根據(jù)官方文檔解釋:TestDriven.NET是Visual Studio的一個TDD插件,最近版本是TestDriven.NET-3.0.2749 RTM版。其中一些新特性有:支持MSTest、.NET Reflector 6 Pro、VS 2010、Silverlight 4、NUnit 2.5.3,使用項目所用的.NET框架等。 下載地址:  

  這個工具使用起來比VS自帶的單元測試和測試覆蓋功能好用,所以從2008年開始基本就用它作為一個必備的工具使用。關(guān)于它具體的功能和怎么使用,我們這里不詳細介紹,網(wǎng)上也有很多文章,大家可以做一下參考和研究。下圖是安裝后以插件的形式出現(xiàn)在VS中的效果:

 

A,基本介紹

  TestDriven.NET原來叫做NUnitAddIn,它是個Visual Studio插件,集成了如下測試框架:NUnit、MbUnit、 ZaneBug、MSTest、NCover、NCoverExplorer、Reflector、TypeMock、dotTrace和MSBee,它主要面向使用TDD的開發(fā)者,主要特性列舉如下:

單鍵運行方法、類、命名空間、項目和解決方案中的單元測試

能夠快速測試實例方法、靜態(tài)方法或?qū)傩?/p>

可以直接跳到.NET Reflector中的任何方法、類型、項目或引用中,這個功能提供了相當(dāng)大的方便

在調(diào)試過程中可以查看.NET Reflector中的任何模塊或堆棧信息

支持多種單元測試框架,包括NUnit、MbUnit、xUnit和MSTest

測試運行在自己的進程中以消除其他問題和邊際效應(yīng)

可以輕松對任何目標(biāo)測試進行調(diào)試或執(zhí)行代碼覆蓋率測試(比微軟自帶的單元測試和代碼覆蓋功能要好用多了)

支持所有主流的.NET語言:C#、VB、C++和F#

B,TestDriven.NET 3.0中的新特性:

TestDriven.Net是基于.NET框架的。再由于VS 2010支持使用多個.NET版本,所以支持各個VS版本和工具就沒有問題了

完全支持在VS 2008和VS 2010中使用MSTest

完全支持.NET Reflector 6 Pro

支持NUnit 2.5.3

支持和兼容VS 2005、VS 2008、VS 2010幾個版本

支持Silverlight 4的測試

C,兼容性

  TestDriven.NET兼容于如下VS版本:Windows XP、Vista、Windows 7、Windows 2000、Windows 2003和Windows 2008(32和64位)上的Visual Studio 2005、2008和2010。官方已經(jīng)不再對VS 2003支持。

D,版本

企業(yè)版:每臺機器一個許可認證

專業(yè)版:一般的許可形式

個人版:面向?qū)W生、開源開發(fā)者和試驗用戶的免費許可(大家可以下載這個版本,個人感覺很好用)

4,關(guān)于本篇

  本篇文章沒有明確的寫作意圖,只是最近在深入研究MONO源碼時有感而發(fā),當(dāng)然作者本人也只是起到了一個研究者或者剖析者的角色。首先實現(xiàn)最簡單且基本的DependencyProperty.Register功能,然后再實現(xiàn)DependencyObject的GetValue和SetValue,接著實現(xiàn)PropertyMetadata的DefaultValue、PropertyChangedCallback、CoerceValueCallback等功能,然后完善DependencyProperty.Register注冊時添加ValidateValueCallback、RegisterAttached、RegisterAttachedReadOnly、RegisterReadOnly、OverrideMetadata、GetMetadata和AddOwner等相關(guān)功能。既然有了這些功能,自然就需要完善PropertyMetadata的IsSealed、Merge和OnApply等相關(guān)底層操作。當(dāng)然在中間還需要DependencyObject的ClearValue、CoerceValue、GetLocalValueEnumerator、ReadLocalValue以及其他的Helper類,這里就不一一進行說明。對于邊際交互比較多且關(guān)聯(lián)比較大的操作,采用了Mock進行暫時模擬,在開發(fā)完了以后再進行了替換。在開發(fā)過程中,隨時進行單元測試和覆蓋率的檢查,這樣可以方便查看哪些功能還有問題以及整體的進度和質(zhì)量的監(jiān)控。

六. DependencyProperty測試代碼

在寫DependencyProperty測試代碼之前,我們先看一下它到底有哪些成員和方法,如下圖:

 

  了解了上面DependencyProperty的基本功能,我們首先創(chuàng)建一個繼承自DependencyObject的類ObjectPoker,由于DependencyObject還沒有被創(chuàng)建,所以我們這里就先創(chuàng)建它,然后在ObjectPoker類里面實現(xiàn)我們的經(jīng)典語句DependencyProperty.Register,由于Register有很多重載,為了方便TDD,就從最簡單的開始(三個參數(shù),不牽涉到元數(shù)據(jù)類),然后再創(chuàng)建一個ObjectPoker的子類,這是方便后面測試DependencyProperty的相關(guān)功能。

class ObjectPoker : DependencyObject
{
//注冊依賴屬性property1
public static readonly DependencyProperty TestProp1 = DependencyProperty.Register("property1", typeof(string), typeof(ObjectPoker));
}
class SubclassPoker : ObjectPoker
{
}

  經(jīng)過上面的測試用例通過以后,自然DependencyProperty.Register的基本功能也就完善了,然后我們來測試一下Register兩個相同的依賴屬性有什么反應(yīng),由于我們?yōu)榱藢崿F(xiàn)Register時沒有考慮那么多,所以測試是先會失敗,然后在引入鍵值對的形式來存儲DependencyProperty,然后每個DependencyProperty都用Name.GetHashCode() ^ PropertyType.GetHashCode() ^ OwnerType.GetHashCode()來區(qū)別唯一,所以相同下面的測試用例也將完成。

[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestMultipleRegisters()
{
//測試注冊相同名的依賴屬性
DependencyProperty.Register("p1", typeof(string), typeof(ObjectPoker));
DependencyProperty.Register("p1", typeof(string), typeof(ObjectPoker));
}

  我們說到依賴屬性系統(tǒng),其實依賴屬性要依附于DependencyObject才能成為真正的依賴屬性系統(tǒng)。所以我們來測試一下AddOwner,每一個Owner都有自己的元數(shù)據(jù),這個時候我們需要完善OverrideMetadata方法,然而OverrideMetadata方法需要用到PropertyMetadata類作為參數(shù),同時需要調(diào)用PropertyMetadata類的DoMerge方法,我們可以創(chuàng)建該類,然后結(jié)合Mock完成該操作。

 

[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestMultipleAddOwner()
{
//測試AddOwner,添加相同類型的Owner
ObjectPoker.TestProp1.AddOwner(typeof(SubclassPoker), new PropertyMetadata());
ObjectPoker.TestProp1.AddOwner(typeof(SubclassPoker), new PropertyMetadata());
}

通過上面的測試用例以后,其實PropertyMetadata的原型已經(jīng)具備了,然后我們要做的就是測試DependencyProperty的默認元數(shù)據(jù)和默認元數(shù)據(jù)的默認值。

[Test]
public void TestDefaultMetadata()
{
//測試默認元數(shù)據(jù)
DependencyProperty p;
p = DependencyProperty.Register("TestDefaultMetadata1", typeof(string), typeof(ObjectPoker));
Assert.IsNotNull(p.DefaultMetadata);

//測試元數(shù)據(jù)的默認值
p = DependencyProperty.Register("TestDefaultMetadata2", typeof(string), typeof(ObjectPoker), new PropertyMetadata("hi"));
Assert.IsNotNull(p.DefaultMetadata);
Assert.AreEqual("hi", p.DefaultMetadata.DefaultValue);
}

  我們都知道一個DependencyProperty可以擁有多個Owner,每個Owner之間的區(qū)別就是用PropertyMetadata,那么這里就給該DependencyProperty添加一個Owner,然后通過該Owner來獲取元數(shù)據(jù)。

[Test]
public void TestAddOwnerNullMetadata()
{
//首先注冊一個依賴屬性,然后再AddOwner,最后根據(jù)新的Owner獲取元數(shù)據(jù)
DependencyProperty p = DependencyProperty.Register("TestAddOwnerNullMetadata", typeof(string), typeof(ObjectPoker));
p.AddOwner(typeof(SubclassPoker), null);

PropertyMetadata pm = p.GetMetadata(typeof(SubclassPoker));
Assert.IsNotNull(pm);
}

  通過上面的測試用例,我們牽涉到了OverrideMetadata方法,當(dāng)然上面沒有進行實現(xiàn),這個時候我們可以來實現(xiàn)OverrideMetadata這個方法,首先注冊一個ObjectPoker類型的依賴屬性,然后通過SubclassPoker來OverrideMetadata。

//首先注冊一個依賴屬性,然后再OverrideMetadata
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void TestOverrideMetadataNullMetadata()
{
//有Type但PropertyMetadata為null時,OverrideMetadata操作
DependencyProperty p = DependencyProperty.Register("TestOverrideMetadataNullMetadata", typeof(string), typeof(ObjectPoker));
p.OverrideMetadata(typeof(SubclassPoker), null);
}

  上面實現(xiàn)了OverrideMetadata的函數(shù),但是只是簡單實現(xiàn),這里我們可以傳入一個null類型的Type作為測試,當(dāng)然測試不會通過,然后就修改代碼直到測試通過吧!

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void TestOverrideMetadataNullType()
{
//當(dāng)Type為null,OverrideMetadata操作
DependencyProperty p = DependencyProperty.Register("TestOverrideMetadataNullType", typeof(string), typeof(ObjectPoker));
p.OverrideMetadata(null, new PropertyMetadata());
}

  如果仔細分析DependencyProperty的源碼,你會發(fā)現(xiàn)有一個DependencyPropertyKey類,這個類到底是干嘛的呢?其實這個類的主要作用就是構(gòu)造函數(shù)傳入該DependencyProperty,然后通過Type來OverrideMetadata,這里只是提供了一個簡單的封裝,如果沒有這個類,其他功能照樣正常。

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void TestReadonlyOverrideMetadata()
{
//通過DependencyPropertyKey的方式OverrideMetadata
DependencyPropertyKey ro_key = DependencyProperty.RegisterReadOnly("readonly-prop1",
typeof(double),
typeof(ObjectPoker),
new PropertyMetadata(double.NaN));
ro_key.DependencyProperty.OverrideMetadata(typeof(SubclassPoker), new PropertyMetadataPoker());
}

最后我們來測試一樣通過DependencyPropertyKey類來注冊一個ReadOnly的依賴屬性,然后進行OverrideMetadata,基本和上一個測試用例類似。

[Test]
public void TestReadonlyOverrideMetadataFromKey()
{
//通過DependencyPropertyKey的方式OverrideMetadata
DependencyPropertyKey ro_key = DependencyProperty.RegisterReadOnly("readonly-prop2",
typeof(double),
typeof(ObjectPoker),
new PropertyMetadata(double.NaN));
ro_key.OverrideMetadata(typeof(SubclassPoker), new PropertyMetadataPoker()); }

  通過上面的測試用例,DependencyProperty類已經(jīng)基本完成,除了該類,其他諸如DependencyObject、PropertyMetadata、DependencyPropertyKey也已經(jīng)初步完成,所以我們這里先以DependencyProperty作為切入點,那么下面就來看一下剛才創(chuàng)建的DependencyProperty類。

七. DependencyProperty實現(xiàn)代碼

具體代碼如下,我們就不做過多闡述,不過有幾點需要注意:

1,一個依賴屬性可能有多個所有者,所以根據(jù)每個所有者都有自己的元數(shù)據(jù)。

2,依賴屬性私有構(gòu)造函數(shù),作為初始化操作,每個依賴屬性在注冊的時候都會調(diào)用并初始化數(shù)據(jù)

3,為了區(qū)別不同的依賴屬性,Name、PropertyType、OwnerType的哈希值取異。

4,注冊依賴屬性有以下幾個種類:Register、RegisterAttached、RegisterAttachedReadOnly和RegisterReadOnly,所以要區(qū)別對待。

5,由于一個依賴屬性可能有多個Owner,根據(jù)每個Owner都有自己的元數(shù)據(jù),所以要有根據(jù)Owner的AddOwner、GetMetadata和OverrideMetadata的操作。

using System.Collections.Generic;
namespace System.Windows
{
public sealed class DependencyProperty
{
//一個依賴屬性可能有多個所有者,所以根據(jù)每個所有者都有自己的元數(shù)據(jù)
private Dictionary<Type,PropertyMetadata> metadataByType = new
 

Dictionary<Type,PropertyMetadata>();

//聲明一個UnsetValue
public static readonly object UnsetValue = new object ();

//依賴屬性私有構(gòu)造函數(shù),作為初始化操作,每個依賴屬性在注冊的時候都會調(diào)用并初始化數(shù)
 

據(jù)
private DependencyProperty (bool isAttached, string name, Type propertyType, Type
 

ownerType,
PropertyMetadata defaultMetadata,
ValidateValueCallback validateValueCallback)

{
IsAttached = isAttached;
DefaultMetadata = (defaultMetadata == null ? new PropertyMetadata() :
 

defaultMetadata);
Name = name;
OwnerType = ownerType;
PropertyType = propertyType;
ValidateValueCallback = validateValueCallback;
}

internal bool IsAttached { get; set; }
public bool ReadOnly { get; private set; }
public PropertyMetadata DefaultMetadata { get; private set; }
public string Name { get; private set; }
public Type OwnerType { get; private set; }
public Type PropertyType { get; private set; }
public ValidateValueCallback ValidateValueCallback { get; private set; }
//獲取依賴屬性的編號,暫未實現(xiàn),在上一篇"WPF基礎(chǔ)到企業(yè)應(yīng)用系列7——深入剖析依賴
 

屬性"有實現(xiàn),原理是在初始化的時候++
public int GlobalIndex {
get { throw new NotImplementedException (); }
}

//傳入ownerType增加Owner
public DependencyProperty AddOwner(Type ownerType)
{
return AddOwner (ownerType, null);
}

//增加所有者,根據(jù)ownerType和typeMetadata
public DependencyProperty AddOwner(Type ownerType, PropertyMetadata typeMetadata)
{
if (typeMetadata == null) typeMetadata = new PropertyMetadata ();
 

OverrideMetadata (ownerType, typeMetadata);

// MS seems to always return the same DependencyProperty
return this;
}

//獲取元數(shù)據(jù),依據(jù)forType
public PropertyMetadata GetMetadata(Type forType)
{
if (metadataByType.ContainsKey (forType))
return metadataByType[forType];
return null;
}

//獲取元數(shù)據(jù),依據(jù)該依賴屬性
public PropertyMetadata GetMetadata(DependencyObject d)
{
if (metadataByType.ContainsKey (d.GetType()))
return metadataByType[d.GetType()];
return null;
}

//獲取元數(shù)據(jù),依據(jù)dependencyObjectType
public PropertyMetadata GetMetadata(DependencyObjectType dependencyObjectType)
{
if (metadataByType.ContainsKey (dependencyObjectType.SystemType))
 

return metadataByType[dependencyObjectType.SystemType];
return null;
}

//驗證類型是否有效
public bool IsValidType(object value)
{
return PropertyType.IsInstanceOfType (value);
}

//驗證值是否有效
public bool IsValidValue(object value)
{
if (!IsValidType (value))
return false;
if (ValidateValueCallback == null)
return true;
return ValidateValueCallback (value);
}

//重寫元數(shù)據(jù),使用PropertyMetadata類的DoMerge方法來操作
public void OverrideMetadata(Type forType, PropertyMetadata typeMetadata)
{
if (forType == null)
throw new ArgumentNullException ("forType");
if (typeMetadata == null)
throw new ArgumentNullException ("typeMetadata");

if (ReadOnly)
throw new InvalidOperationException (String.Format ("Cannot override metadata on
 

readonly property '{0}' without using a DependencyPropertyKey", Name));

typeMetadata.DoMerge (DefaultMetadata, this, forType);
metadataByType.Add (forType, typeMetadata);
}

//重寫元數(shù)據(jù),使用PropertyMetadata類的DoMerge方法來操作
public void OverrideMetadata (Type forType, PropertyMetadata typeMetadata,
 

DependencyPropertyKey key)

{
if (forType == null)
throw new ArgumentNullException ("forType");
if (typeMetadata == null)
throw new ArgumentNullException ("typeMetadata");

typeMetadata.DoMerge (DefaultMetadata, this, forType);
metadataByType.Add (forType, typeMetadata);
}

public override string ToString ()
{
return Name; 125: }

//得到哈希值,區(qū)別不同的依賴屬性,Name、PropertyType、OwnerType的哈希值取異
public override int GetHashCode ()
{
return Name.GetHashCode() ^ PropertyType.GetHashCode() ^ OwnerType.GetHashCode
 

();
}

//注冊依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type)
public static DependencyProperty Register(string name, Type propertyType, Type
 

ownerType)
{
return Register(name, propertyType, ownerType, null, null); 137: }

//注冊依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type、元數(shù)據(jù))
public static DependencyProperty Register(string name, Type propertyType, Type
 

ownerType,
PropertyMetadata typeMetadata)
{
return Register(name, propertyType, ownerType, typeMetadata, null);
}

//注冊依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type、元數(shù)據(jù)、驗證
 

回調(diào)委托) 147: public static DependencyProperty Register(string name, Type
 

propertyType, Type ownerType,
PropertyMetadata typeMetadata,
ValidateValueCallback validateValueCallback)
{
if (typeMetadata == null)
typeMetadata = new PropertyMetadata();

DependencyProperty dp = new DependencyProperty(false, name, propertyType,
 

ownerType,
typeMetadata, validateValueCallback);
DependencyObject.register(ownerType, dp);

dp.OverrideMetadata (ownerType, typeMetadata);

return dp; 161: }

//注冊附加依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type)
public static Dtached(string name, Type propertyType, Type ownerType)
{
return RegisterAttached(name, propertyType, ownerType, null, null);
}
//注冊附加依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type、元數(shù)據(jù))
public static DependencyProperty RegisterAttached(string name, Type
 

propertyType, Type ownerType,
PropertyMetadata defaultMetadata)
{
return RegisterAttached(name, propertyType, ownerType, defaultMetadata, null);
}

//注冊附加依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type、元數(shù)據(jù)、驗證回調(diào)
 

委托)
public static DependencyProperty RegisterAttached(string name, Type propertyType,
 

Type ownerType,
PropertyMetadata defaultMetadata,
ValidateValueCallback validateValueCallback)
{
DependencyProperty dp = new DependencyProperty(true, name, propertyType,
 

ownerType, defaultMetadata, validateValueCallback);
 

DependencyObject.register(ownerType, dp);
return dp;
}

//注冊只讀依賴屬性,暫未實現(xiàn)
public static DependencyPropertyKey RegisterAttachedReadOnly(string name,
 

Type propertyType, Type ownerType,
PropertyMetadata defaultMetadata)
{
throw new NotImplementedException("RegisterAttachedReadOnly(string name, Type
 

propertyType, Type ownerType, PropertyMetadata defaultMetadata)");
}

//注冊只讀依賴屬性,暫未實現(xiàn)
public static DependencyPropertyKey RegisterAttachedReadOnly(string name,

Type propertyType, Type ownerType,
PropertyMetadata defaultMetadata,
ValidateValueCallback validateValueCallback)
{
throw new NotImplementedException("RegisterAttachedReadOnly(string name, Type

propertyType, Type ownerType, PropertyMetadata defaultMetadata, ValidateValueCallback

validateValueCallback)");
}

//注冊只讀依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type、元數(shù)據(jù))
public static DependencyPropertyKey RegisterReadOnly(string name, Type

propertyType, Type ownerType,
PropertyMetadata typeMetadata)
{
return RegisterReadOnly (name, propertyType, ownerType, typeMetadata,

null);
}
//注冊只讀依賴屬性(參數(shù):依賴屬性名、依賴屬性的Type、擁有者的Type、元數(shù)據(jù)、
 

驗證回調(diào)委托)
public static DependencyPropertyKey RegisterReadOnly(string name, Type
 

propertyType, Type ownerType,
PropertyMetadata typeMetadata,
ValidateValueCallback validateValueCallback)
{
DependencyProperty prop = Register (name, propertyType, ownerType,
 

typeMetadata, validateValueCallback);
prop.ReadOnly = true;
return new DependencyPropertyKey (prop);
}

}
}

通過前面的步驟,DependencyProperty已經(jīng)完成,那么下面我們再來看一下DependencyObject類。

八. DependencyObject測試代碼

在寫DependencyObject測試代碼之前,我們先看一下它到底有哪些成員和方法,如下圖:

 

  通過上面的這幅圖,我們知道它的主要功能包括:各種依賴屬性的GetValue、SetValue操作(核心功能)和ClearValue、CoerceValue、GetLocalValueEnumerator、ReadLocalValue等操作。為了測試這些功能,我們首先創(chuàng)建幾個類,第一個類X,內(nèi)部首先注冊一個附加依賴屬性,我們都知道,不管是附加依賴屬性還是依賴屬性,都需要使用到GetValue和SetValue操作,只是一個封裝成了屬性,而另一個封裝成了靜態(tài)方法而已。第二個類直接繼承自我們前面在實現(xiàn)DependencyProperty時創(chuàng)建的DependencyObject原型類。

class X {
//注冊一個附加依賴屬性A
public static readonly DependencyProperty AProperty = DependencyProperty.RegisterAttached("A", typeof(int), typeof(X));
//獲取附加屬性A的值
public static void SetA(DependencyObject obj, int value)
{
obj.SetValue(AProperty, value);
}
//設(shè)置附加屬性A的值
public static int GetA(DependencyObject obj)
{
return (int)obj.GetValue(AProperty);
}
//注冊一個附加依賴屬性B
public static readonly DependencyProperty BProperty = DependencyProperty.RegisterAttached("B", typeof(string), typeof(X));
//設(shè)置附加屬性B的值
public static void SetB(DependencyObject obj, string value)
{
obj.SetValue(BProperty, value);
}
//獲取附加屬性B的值
public static string GetB(DependencyObject obj)
{
return (string)obj.GetValue(BProperty);
}

}

class Y : DependencyObject {
}

第三個類則是為了直接測試注冊一個依賴屬性,這個類首先繼承自DependencyObject原型類。

class Z : DependencyObject
{
public static readonly DependencyProperty SimpleDPProperty =
DependencyProperty.Register("SimpleDP", typeof(double), typeof(Z),
new PropertyMetadata((double)0.0,
new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(CoerceValue)),
new ValidateValueCallback(IsValidValue));

public double SimpleDP
{
get { return (double)GetValue(SimpleDPProperty); }
set { SetValue(SimpleDPProperty, value); }
}

private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("當(dāng)值改變時,我們可以做的一些操作,具體可以在這里定義: {0}", e.NewValue);
}

private static object CoerceValue(DependencyObject d, object value)
{
Console.WriteLine("對值進行限定,強制值: {0}", value);
return value;
}

private static bool IsValidValue(object value)
{
Console.WriteLine("驗證值是否通過,如果返回True表示驗證通過,否則會以異常的形式暴露: {0}", value);
return true;
}

}

首先我們先寫測試GetValue和SetValue操作的測試代碼,然后不能通過,最后完善DependencyObject類的GetValue和SetValue方法直到測試用例通過。

[Test]
[Category ("NotWorking")]
public void TestAttachedProperty()
{
Y y1 = new Y();
X.SetA(y1, 2);
Assert.AreEqual(2, X.GetA(y1));
}

由于這里是y1和y2兩個對象,所以他們的GetValue和SetValue也是設(shè)置和取得各自的值。

[Test]
[Category ("NotWorking")]
public void Test2AttachedProperties()
{
Y y1 = new Y();
Y y2 = new Y();
X.SetA(y1, 2);
X.SetA(y2, 3);
Assert.AreEqual(2, X.GetA(y1));
Assert.AreEqual(3, X.GetA(y2));
}

  通過前面的圖,大家可以看到DependencyObject提供了一個取得本地值枚舉器的GetLocalValueEnumerator方法,它實現(xiàn)一個IEnumerator來方便訪問LocalValue,這里我們要實現(xiàn)它,所以先寫測試代碼。

[Test]
[Category ("NotWorking")]
public void TestEnumerationOfAttachedProperties()
{
int count = 0;
Y y = new Y();
X.SetA(y, 2);
X.SetB(y, "Hi");

//根據(jù)DependencyObject得到所有本地值
LocalValueEnumerator e = y.GetLocalValueEnumerator();
while (e.MoveNext()) {
count++;
if (e.Current.Property == X.AProperty)
Assert.AreEqual(e.Current.Value, 2);
else if (e.Current.Property == X.BProperty)
Assert.AreEqual(e.Current.Value, "Hi");
else
Assert.Fail("Wrong sort of property" + e.Current.Property);
}
//count為2
Assert.AreEqual(2, count);
}

還有幾個功能,既然Mono也沒做研究,我們也就不費那個力氣了,接下來我們就看看剛才實現(xiàn)的DependencyObject代碼吧!

九. DependencyObject實現(xiàn)代碼

通過前面的測試用例,DependencyObject類的基本功能已經(jīng)完成,不過我們要注意幾個要點:
1,依賴屬性其實終究要DependencyObject和DependencyProperty成對才能算得上真正的DependencyProperty

2,不管是Register、RegisterAttached、RegisterAttachedReadOnly還是RegisterReadOnly操作,我們都要通過DependencyObject來操作DependencyProperty的值,也就是通過DependencyObject這個外部接口來操作,DependencyProperty只負責(zé)注冊和內(nèi)部處理,不負責(zé)外部接口。

3,在DependencyObject中提供了幾個操作LocalValue的接口的接口,其中包括ReadLocalValue、GetLocalValueEnumerator、CoerceValue和ClearValue等。

4,在注冊注冊依賴屬性時,實質(zhì)是關(guān)聯(lián)DependencyObject的propertyDeclarations,它是一個Dictionary<Type,Dictionary<string,DependencyProperty>>類型,但是在register代碼中并沒有完全關(guān)聯(lián)起來,我也比較納悶,所以這點還希望和大家一起探討,微軟的BCL并沒有這么實現(xiàn)。

//using System.Windows.Threading;

namespace System.Windows
{
public class DependencyObject
{
//依賴屬性其實終究要DependencyObject和DependencyProperty成對才能算得上真
 

正的DependencyProperty
private static Dictionary<Type,Dictionary<string,DependencyProperty>>
 

propertyDeclarations = new Dictionary<Type,Dictionary<string,DependencyProperty>>();
 

//該依賴屬性的鍵值對,鍵為DependencyProperty,值為object
private Dictionary<DependencyProperty,object> properties = new
 

Dictionary<DependencyProperty,object>();

//是否已密封,沒有實現(xiàn)DependencyObject層次的IsSealed判斷
public bool IsSealed {
get { return false; }
}

//獲取該DependencyObject的DependencyObjectType
public DependencyObjectType DependencyObjectType {
get { return DependencyObjectType.FromSystemType (GetType()); }
}

//根據(jù)該依賴屬性名,清除它的值
public void ClearValue(DependencyProperty dp)
{
if (IsSealed)
throw new InvalidOperationException ("Cannot manipulate property values
 

on a sealed DependencyObject");

properties[dp] = null;
}

//根據(jù)該依賴屬性DependencyPropertyKey,清除它的值
public void ClearValue(DependencyPropertyKey key)
{
ClearValue (key.DependencyProperty);
}

//根據(jù)該依賴屬性名,強制值
public void CoerceValue (DependencyProperty dp)
{
PropertyMetadata pm = dp.GetMetadata (this);
if (pm.CoerceValueCallback != null)
pm.CoerceValueCallback (this, GetValue (dp));
}

public sealed override bool Equals (object obj)
{
throw new NotImplementedException("Equals");
}

public sealed override int GetHashCode ()
{
throw new NotImplementedException("GetHashCode");
}

//得到本地值的枚舉器
public LocalValueEnumerator GetLocalValueEnumerator()
{
return new LocalValueEnumerator(properties);
}

//根據(jù)依賴屬性名獲取值
public object GetValue(DependencyProperty dp)
{
object val = properties[dp];
return val == null ? dp.DefaultMetadata.DefaultValue : val;
}


public void InvalidateProperty(DependencyProperty dp)
{
throw new NotImplementedException("InvalidateProperty(DependencyProperty dp)");
}

//當(dāng)屬性值改變時,觸發(fā)回調(diào)
protected virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
PropertyMetadata pm = e.Property.GetMetadata (this);
if (pm.PropertyChangedCallback != null)
pm.PropertyChangedCallback (this, e);
}

//提供一個外界查看LocalValue的接口
public object ReadLocalValue(DependencyProperty dp)
{
object val = properties[dp];
return val == null ? DependencyProperty.UnsetValue : val;
}

//根據(jù)依賴屬性名設(shè)置其值
public void SetValue(DependencyProperty dp, object value)
{
if (IsSealed)
throw new InvalidOperationException ("Cannot manipulate property values on a
 

sealed DependencyObject");

if (!dp.IsValidType (value))
throw new ArgumentException ("value not of the correct type for this
 

DependencyProperty");

ValidateValueCallback validate = dp.ValidateValueCallback;
if (validate != null && !validate(value))
throw new Exception("Value does not validate");
else
properties[dp] = value;
}

//根據(jù)依賴屬性DependencyPropertyKey設(shè)置其值
public void SetValue(DependencyPropertyKey key, object value)
{
SetValue (key.DependencyProperty, value);
}

protected virtual bool ShouldSerializeProperty (DependencyProperty dp) {
throw new NotImplementedException ();
}

//這里的注冊實質(zhì)是關(guān)聯(lián)propertyDeclarations
internal static void register(Type t, DependencyProperty dp)
{
if (!propertyDeclarations.ContainsKey (t))
propertyDeclarations[t] = new Dictionary<string,DependencyProperty>();
Dictionary<string,DependencyProperty> typeDeclarations =
 

propertyDeclarations[t];
if (!typeDeclarations.ContainsKey(dp.Name))
{
typeDeclarations[dp.Name] = dp;
//這里仍然有一些問題,期待各位共同探討解決
}
else
throw new ArgumentException("A property named " + dp.Name + " already
 

exists on " + t.Name);
}
}
}

  通過前面對DependencyObject和DependencyProperty的研究之后,我們來看看最重要的一個角色,這也是微軟最喜歡用的概念——元數(shù)據(jù),如果大家研究過微軟BCL的源碼,應(yīng)該都知道,它是貫穿于整個CLR當(dāng)中的。

十. PropertyMetadata測試代碼

前面我們看到一個依賴屬性的注冊最全的形式是下面這樣子的:

public static DependencyProperty Register(string name,
Type propertyType,
Type ownerType,
PropertyMetadata typeMetadata,
ValidateValueCallback validateValueCallback);

  第一個參數(shù)是該依賴屬性的名字,第二個參數(shù)是依賴屬性的類型,第三個參數(shù)是該依賴屬性的所有者的類型,第五個參數(shù)就是一個驗證值的回調(diào)委托,那么最使我們感興趣的還是這個可愛的 PropertyMetadata ,也就是我們接下來要講的元數(shù)據(jù)。 提到WPF屬性元數(shù)據(jù),大家可能第一想到的是剛才的PropertyMetadata,那么這個類到底是怎樣的呢?我們應(yīng)該怎樣使用它呢?首先我們看它的構(gòu)造函數(shù)(我們選參數(shù)最多的來講):

public PropertyMetadata(object defaultValue,
PropertyChangedCallback propertyChangedCallback,
CoerceValueCallback coerceValueCallback);

  其中的第一個參數(shù)是默認值,最后兩個分別是PropertyChanged(變化通知)以及Coerce(強制)的兩個委托變量,我們在實例化的時候,只需要把這兩個委托變量關(guān)聯(lián)到具體的方法上即可。

  事實上,除了PropertyMetadata以外,常見的還有FrameworkPropertyMetadata,UIPropertyMetadata。他們的繼承關(guān)系是F->U->P。其中以FrameworkPropertyMetadata參數(shù)最多,亦最為復(fù)雜。

FrameworkPropertyMetadata的構(gòu)造函數(shù)提供了很多重載,我們挑選最為復(fù)雜的重載來看它到底有哪些參數(shù)以及提供了哪些功能:

public FrameworkPropertyMetadata(object defaultValue,
FrameworkPropertyMetadataOptions flags,
PropertyChangedCallback propertyChangedCallback,
CoerceValueCallback coerceValueCallback,
bool isAnimationProhibited,
UpdateSourceTrigger defaultUpdateSourceTrigger);

  其中第一個參數(shù)是默認值,最后兩個參數(shù)分別是是否允許動畫,以及綁定時更新的策略(在Binding當(dāng)中相信大家并不陌生),這個不詳細解釋了。重點看一下里第三、四兩個參數(shù),兩個 CallBack的委托。結(jié)合前面Register的時候提到的ValidateValueCallback共組成三大”金剛“,這三個Callback分別代表Validate(驗證),PropertyChanged(變化通知)以及Coerce(強制)。當(dāng)然,作為 Metadata,F(xiàn)rameworkPropertyMetadata只是儲存了該依賴屬性的策略信息,WPF屬性系統(tǒng)會根據(jù)這些信息來提供功能并在適當(dāng)?shù)臅r機回調(diào)傳入的delegate,所以最重要的還是我們定義的這些方法,通過他們傳入委托才能起到真正的作用。

具體PropertyMetadata包含哪些成員呢?我們先看微軟的PropertyMetadata類

 

  在寫其他測試用例之前,我們先來創(chuàng)建兩個類,第一個類TestDepObj,內(nèi)部注冊了四個依賴屬性,前三個沒有元數(shù)據(jù)操作,也就是沒有顯示聲明并構(gòu)造元數(shù)據(jù)類,第四個添加了一個元數(shù)據(jù)類,這個元數(shù)據(jù)類包含了默認值、值改變回調(diào)委托、強制值回調(diào)委托。第二個類TestSubclass繼承自TestDepObj。

class TestDepObj : DependencyObject
{
public static readonly DependencyProperty TestProp1 = DependencyProperty.Register("property1", typeof(string), typeof(TestDepObj));
public static readonly DependencyProperty TestProp2 = DependencyProperty.Register("property2", typeof(string), typeof(TestDepObj));
public static readonly DependencyProperty TestProp3 = DependencyProperty.Register(&quot;property3", typeof(string), typeof(TestDepObj));

public static readonly DependencyProperty TestProp4 = DependencyProperty.Register("property4", typeof(string), typeof(TestDepObj), new PropertyMetadata("default", changed, coerce));

static void changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
static object coerce(DependencyObject d, object baseValue) { return baseValue; }
}

class TestSubclass : TestDepObj
{
}

  大家看到我們在創(chuàng)建PropertyMetadata的時候?qū)δ承┕δ懿]有實現(xiàn),這里我們就通過子類來具體實現(xiàn),MONO的這種做法想沿襲微軟PropertyMetadata、FrameworkPropertyMetadata和UIPropertyMetadata的做法,但是個人覺得它實現(xiàn)得并不是太好,很多地方感覺很別扭。

//首先我們自定義一個元數(shù)據(jù)類,繼承自我們剛創(chuàng)建的PropertyMetadata類
public class PropertyMetadataPoker : PropertyMetadata
{

public bool BaseIsSealed
{
get { return base.IsSealed; }
}

public void CallApply()
{
OnApply(TestDepObj.TestProp1, typeof(TestDepObj));
}

public void CallMerge(PropertyMetadata baseMetadata, DependencyProperty dp)
{
Merge(baseMetadata, dp);
}

protected override void Merge(PropertyMetadata baseMetadata, DependencyProperty dp)
{
Console.WriteLine(Environment.StackTrace);
base.Merge(baseMetadata, dp);
}

protected override void OnApply(DependencyProperty dp, Type targetType) {
//
base.OnApply(dp, targetType);
Console.WriteLine("IsSealed in OnApply? {0}", IsSealed);
Console.WriteLine(Environment.StackTrace);
}
}

  下面的測試代碼主要看一下元數(shù)據(jù)的默認值,實例化一個元數(shù)據(jù)類,然后調(diào)用它的DefaultValue、PropertyChangedCallback、CoerceValueCallback,測試他們是否為Null。

[Test]
public void DefaultValues()
{
//首先看看元數(shù)據(jù)的默認值
PropertyMetadataPoker m = new PropertyMetadataPoker();
Assert.AreEqual(null, m.DefaultValue);
Assert.AreEqual(null, m.PropertyChangedCallback);
Assert.AreEqual(null, m.CoerceValueCallback);
}

  我們在WPF和Silverlight中都有過這樣的體會:到底什么時候這個依賴屬性不能再修改了,其實這個操作得歸功于OnApply什么時候觸發(fā),我們也可以調(diào)用IsSealed來查看,那么這里我們就先寫測試代碼。第一段代碼直接顯示調(diào)用CallApply方法進行密封;第二段代碼則是通過OverrideMetadata操作后內(nèi)部調(diào)用的CallApply;第三段代碼是通過AddOwner操作中調(diào)用的CallApply;最后一段代碼通過調(diào)用DependencyProperty.Register時傳入元數(shù)據(jù),在其內(nèi)部調(diào)用CallApply。

[Test]
public void IsSealed()
{
//測試元數(shù)據(jù)是否密封,這個很重要,因為封閉之后就不能修改了,除非用OverrideMetadata或者AddOwner
PropertyMetadataPoker m;

Console.WriteLine(1);
// 直接調(diào)用 OnApply 查看元數(shù)據(jù)是否密封
m = new PropertyMetadataPoker();
Assert.IsFalse(m.BaseIsSealed);
m.CallApply();
Assert.IsFalse(m.BaseIsSealed);

Console.WriteLine(2);
// 直接 OverrideMetadata
m = new PropertyMetadataPoker();
TestDepObj.TestProp1.OverrideMetadata(typeof(TestSubclass), m);
Assert.IsTrue(m.BaseIsSealed);

Console.WriteLine(3);
// 調(diào)用 DependencyProperty.AddOwner, 通過這種方式 OverrideMetadata
m = new PropertyMetadataPoker();
TestDepObj.TestProp2.AddOwner(typeof(TestSubclass), m);
Assert.IsTrue(m.BaseIsSealed);
Console.WriteLine(4);
// 最后, 調(diào)用DependencyProperty.Register時傳入元數(shù)據(jù)
m = new PropertyMetadataPoker();
DependencyProperty.Register("xxx", typeof(string), typeof(TestDepObj), m); Assert.IsTrue(m.BaseIsSealed);
}

下面這段測試代碼是驗證AddOwner后的DependencyProperty是否和原來的DependencyProperty是同一個DependencyProperty。

[Test]
public void TestAddOwnerResult()
{
//測試AddOwner后的DependencyProperty是否和原來的DependencyProperty是同一個DependencyProperty
PropertyMetadataPoker m = new PropertyMetadataPoker();
DependencyProperty p = TestDepObj.TestProp3.AddOwner(typeof(TestSubclass), m);

//結(jié)果是同一個DependencyProperty
Assert.AreSame(p, TestDepObj.TestProp3);
}

  下面這個測試用例是首先實例化元數(shù)據(jù)并作為注冊依賴屬性時的參數(shù)傳入,大家都知道此時如果想修改元數(shù)據(jù),可以通過AddOwner或者OverrideMetadata,如果直接賦值,會拋出錯誤,因為元數(shù)據(jù)已經(jīng)密封。

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void ModifyAfterSealed1()
{
//首先實例化元數(shù)據(jù)并注冊依賴屬性時作為參數(shù)傳入
PropertyMetadataPoker m = new PropertyMetadataPoker();
DependencyProperty.Register("p1", typeof(string), typeof(TestDepObj), m);
Assert.IsTrue(m.BaseIsSealed);

//由于元數(shù)據(jù)已密封,所以拋出如下錯誤信息:Cannot change metadata once it has been applied to a property
m.CoerceValueCallback = null;
}

這個和上面的那個測試用例基本一樣,只不過把CoerceValueCallback換成了PropertyChangedCallback

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void ModifyAfterSealed2()
{
//首先實例化元數(shù)據(jù)并注冊依賴屬性時作為參數(shù)傳入
PropertyMetadataPoker m = new PropertyMetadataPoker();
DependencyProperty.Register("p2", typeof(string), typeof(TestDepObj), m); Assert.IsTrue(m.BaseIsSealed);

//由于元數(shù)據(jù)已密封,所以拋出如下錯誤信息:Cannot change metadata once it has been applied to a property
m.PropertyChangedCallback = null;

下面這個測試用例也和上面的兩個測試用例類似,它是修改元數(shù)據(jù)的DefaultValue

[Test]
[ExpectedException(typeof(InvalidOperationException))]
public void ModifyAfterSealed3()
{
//首先實例化元數(shù)據(jù)并注冊依賴屬性時作為參數(shù)傳入
PropertyMetadataPoker m = new PropertyMetadataPoker();
DependencyProperty.Register("p3", typeof(string), typeof(TestDepObj), m);
Assert.IsTrue(m.BaseIsSealed);

//由于元數(shù)據(jù)已密封,所以拋出如下錯誤信息:Cannot change metadata once it has been applied to a property
m.DefaultValue = "hi"; 12: }

  通過前面的測試用例,大家可能都會發(fā)現(xiàn)有一個Merge這個方法,它在什么時候調(diào)用呢?其實它在OverrideMetadata和AddOwner操作中都會調(diào)用,在

DependencyProperty中的Register也會顯示調(diào)用一次。我們需要注意的是:在元數(shù)據(jù)密封了以后就會拋出錯誤。

[Test]
public void TestMerge()
{
//需要注意的是:在元數(shù)據(jù)密封了以后就會拋出錯誤
PropertyMetadataPoker m = new PropertyMetadataPoker();
m.CallMerge(TestDepObj.TestProp4.GetMetadata(typeof(TestDepObj)), TestDepObj.TestProp4);
Assert.AreEqual("default", m.DefaultValue);
Assert.IsNotNull(m.CoerceValueCallback);
Assert.IsNotNull(m.PropertyChangedCallback);

m = new PropertyMetadataPoker();
m.DefaultValue = "non-default";
m.CallMerge(TestDepObj.TestProp4.GetMetadata(typeof(TestDepObj)), TestDepObj.TestProp4);
Assert.AreEqual("non-default", m.DefaultValue);
Assert.IsNotNull(m.CoerceValueCallback);
Assert.IsNotNull(m.PropertyChangedCallback);

//我們知道元數(shù)據(jù)包括DefaultValue、 coerce 和 property changed等
//這里我們就不一一測試了,其他測試結(jié)果都是一樣的
}

下面的測試用例主要是默認值是不能被設(shè)置成Unset的

[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestSetDefaultToUnsetValue()
{
//默認值是不能被設(shè)置成Unset的
PropertyMetadata m = new PropertyMetadata();
m.DefaultValue = DependencyProperty.UnsetValue;
}

[Test]
[ExpectedException(typeof(ArgumentException))]
public void TestInitDefaultToUnsetValue()
{
//默認值是不能被設(shè)置成Unset的
new PropertyMetadata(DependencyProperty.UnsetValue);
}

通過前面的多個測試用例,其實已經(jīng)包含了PropertyMetadata的基本功能,那我們接下來就看一下PropertyMetadata的內(nèi)部設(shè)計和實現(xiàn)。

十一. PropertyMetadata實現(xiàn)代碼

MONO的PropertyMetadata類要比微軟的PropertyMetadata類簡單很多,不過我們也需要注意一下幾點:

1,元數(shù)據(jù)類包含哪些成員以及有幾個構(gòu)造函數(shù)重載?因為這些直接關(guān)系到外部的調(diào)用。

2,大家要注意ValidateValueCallback不是PropertyMetadata的成員,所以在PropertyMetadata的構(gòu)造函數(shù)中不要把它作為參數(shù)傳入。

3,注意OnApply函數(shù),因為調(diào)用它之后就不能修改元數(shù)據(jù)的成員,只有通過OverrideMetadata和AddOwner間接實現(xiàn),如果大家想知道到底這個元數(shù)據(jù)有沒有被密封,可以調(diào)用IsSealed屬性來查看,這個功能我們也會經(jīng)常用到。

4,元數(shù)據(jù)類中提供了Merge的功能,用來方便合并父類和子類的元數(shù)據(jù)。
 

namespace System.Windows
{
//依賴屬性三大回調(diào)委托:PropertyChangedCallback、CoerceValueCallback和
 

ValidateValueCallback
public delegate void PropertyChangedCallback(DependencyObject d,
 

DependencyPropertyChangedEventArgs e);
public delegate object CoerceValueCallback(DependencyObject d, object baseValue);
 


public delegate bool ValidateValueCallback(object value);

public class PropertyMetadata
{
private object defaultValue;
private bool isSealed;
private PropertyChangedCallback propertyChangedCallback;
private CoerceValueCallback coerceValueCallback;

//返回該元數(shù)據(jù)是否已密封
protected bool IsSealed
{
get { return isSealed; }
}

//獲取和設(shè)置元數(shù)據(jù)默認值
public object DefaultValue
{
get { return defaultValue; }
set
{
if (IsSealed)
throw new InvalidOperationException("Cannot change metadata once it has been
 

applied to a property");
if (value == DependencyProperty.UnsetValue)
throw new ArgumentException("Cannot set property metadata's default value to
 

'Unset'");

defaultValue = value;
}
}

//ChangedCallback委托賦值,注意檢查元數(shù)據(jù)是否已經(jīng)密封
public PropertyChangedCallback PropertyChangedCallback
{
get { return propertyChangedCallback; }
set
{
if (IsSealed)
throw new InvalidOperationException("Cannot change metadata once it
 

has been applied to a property");
propertyChangedCallback = value;
}
}

//CoerceValueCallback委托賦值,注意檢查元數(shù)據(jù)是否已經(jīng)密封
public CoerceValueCallback CoerceValueCallback
{
get { return coerceValueCallback; }
set
{
if (IsSealed)
throw new InvalidOperationException("Cannot change metadata once
 

it has been applied to a property");
coerceValueCallback = value;
}
}

#region PropertyMetadata構(gòu)造函數(shù),根據(jù)不同參數(shù)做初始化操作
public PropertyMetadata()
: this(null, null, null)
{
}

public PropertyMetadata(object defaultValue)
: this(defaultValue, null, null)
{
}

public PropertyMetadata(PropertyChangedCallback propertyChangedCallback)
 

: this(null, propertyChangedCallback, null)
{
}

public PropertyMetadata(object defaultValue, PropertyChangedCallback
 

propertyChangedCallback)
: this(defaultValue, propertyChangedCallback, null)
{
}

public PropertyMetadata(object defaultValue, PropertyChangedCallback
 

propertyChangedCallback, CoerceValueCallback coerceValueCallback)
{
if (defaultValue == DependencyProperty.UnsetValue)
throw new ArgumentException("Cannot initialize property metadata's
 

default value to 'Unset'");

this.defaultValue = defaultValue;
this.propertyChangedCallback = propertyChangedCallback;
this.coerceValueCallback = coerceValueCallback;
}
#endregion

//合并元數(shù)據(jù)
protected virtual void Merge(PropertyMetadata baseMetadata, DependencyProperty
 

dp)
{
if (defaultValue == null)
defaultValue = baseMetadata.defaultValue;
if (propertyChangedCallback == null)
propertyChangedCallback = baseMetadata.propertyChangedCallback;
 

if (coerceValueCallback == null)
coerceValueCallback = baseMetadata.coerceValueCallback;
}

protected virtual void OnApply(DependencyProperty dp, Type targetType)
 

{
//留給子類來實現(xiàn)吧!
}

//合并元數(shù)據(jù)并密封
internal void DoMerge(PropertyMetadata baseMetadata, DependencyProperty dp, Type
 

targetType)
{
Merge(baseMetadata, dp);
OnApply(dp, targetType);
isSealed = true;
}
}
}

在上面幾個類就是依賴屬性系統(tǒng)的核心類,下面將看到幾個Helper類。

十二. 其他協(xié)助類測試代碼

這里就簡單寫一下對DependencyObjectTypeTest的測試代碼:

using System;
using System.Windows;
using NUnit.Framework;

namespace TDDDependencyTest.System.Windows
{
[TestFixture]
public class DependencyObjectTypeTest
{

[Test]
public void Accessors()
{
DependencyObjectType t = DependencyObjectType.FromSystemType(typeof(TestDepObj));
Assert.AreEqual("TestDepObj", t.Name);
Assert.AreEqual(typeof(TestDepObj), t.SystemType);
Assert.AreEqual(typeof(DependencyObject), t.BaseType.SystemType); 18: }

[Test]
public void IsInstanceOfType()
{
DependencyObjectType t = DependencyObjectType.FromSystemType(typeof(TestDepObj));
DependencyObjectType t2 = DependencyObjectType.FromSystemType(typeof(TestSubclass));
Assert.IsTrue(t.IsInstanceOfType(new TestSubclass()));
Assert.IsTrue(t2.IsSubclassOf(t));
Assert.IsFalse(t.IsSubclassOf(t2));
}

[Test]
public void TestCache()
{
DependencyObjectType t = DependencyObjectType.FromSystemType(typeof(TestDepObj));
DependencyObjectType t2 = DependencyObjectType.FromSystemType(typeof(TestDepObj));
Assert.AreSame(t, t2);
}
}
}

由于它的功能比較簡單,所以我們就不做過多介紹,大家想了解更多,可以參看代碼。

十三. 其他協(xié)助類的實現(xiàn)代碼

LocalValueEnumerator:手動實現(xiàn)一個IEnumerator來方便訪問LocalValue

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace System.Windows
{
//手動實現(xiàn)一個IEnumerator來方便訪問LocalValue
public struct LocalValueEnumerator : IEnumerator
{
private IDictionaryEnumerator propertyEnumerator;
private Dictionary<DependencyProperty, object> properties;

private int count;

internal LocalValueEnumerator(Dictionary<DependencyProperty, object> properties)
{
this.count = properties.Count;
this.properties = properties;
this.propertyEnumerator = properties.GetEnumerator();
}

public int Count
{
get { return count; }
}

//獲取當(dāng)前LocalValue
public LocalValueEntry Current
{
get
{
return new LocalValueEntry((DependencyProperty)propertyEnumerator.Key,
propertyEnumerator.Value);
}
}

object IEnumerator.Current
{
get { return this.Current; }
}
 

public bool MoveNext()
{
return propertyEnumerator.MoveNext();
}

//重置propertyEnumerator
public void Reset()
{
propertyEnumerator.Reset();
}

public static bool operator !=(LocalValueEnumerator obj1, LocalValueEnumerator obj2)
{
throw new NotImplementedException();
}

public static bool operator ==(LocalValueEnumerator obj1, LocalValueEnumerator obj2)
{
throw new NotImplementedException();
}

public override bool Equals(object obj)
{
throw new NotImplementedException();
}

public override int GetHashCode()
{
throw new NotImplementedException();
}
}

//LocalValue實體類
public struct LocalValueEntry
{
private DependencyProperty property;
private object value;

internal LocalValueEntry(DependencyProperty property, object value)
{
this.property = property;
this.value = value;
}

public DependencyProperty Property
{
get { return property; }
}

public object Value
{
get { return value; }
}

public static bool operator !=(LocalValueEntry obj1, LocalValueEntry obj2)
{
throw new NotImplementedException();
}

public static bool operator ==(LocalValueEntry obj1, LocalValueEntry obj2)
{
throw new NotImplementedException();
}

public override bool Equals(object obj)
{
throw new NotImplementedException();
}

public override int GetHashCode()
{
throw new NotImplementedException();
}
}
}
 

DependencyPropertyChangedEventArgs:PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)的參數(shù),它的第一個參數(shù)為該DependencyProperty、第二個參數(shù)為原來的值、第三個參數(shù)為改變了的值。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows
{
public class DependencyPropertyChangedEventArgs
{
//第一個參數(shù)為該DependencyProperty、第二個參數(shù)為原來的值、第三個參數(shù)為新
 


public DependencyPropertyChangedEventArgs(DependencyProperty property,
 

object oldValue, object newValue)
{
this.Property = property;
this.OldValue = oldValue;
this.NewValue = newValue;
}

//注意所有的屬性只對外界開放只讀操作
public object NewValue
{
get;
private set;
}

public object OldValue
{
get;
private set;
}

public DependencyProperty Property
{
get;
private set;
}

public override bool Equals(object obj)
{
if (!(obj is DependencyPropertyChangedEventArgs))
return false;

return Equals((DependencyPropertyChangedEventArgs)obj);
}

public bool Equals(DependencyPropertyChangedEventArgs args)
{
return (Property == args.Property &&
NewValue == args.NewValue &&
OldValue == args.OldValue);
}

public static bool operator !=(DependencyPropertyChangedEventArgs left,
 

DependencyPropertyChangedEventArgs right)
{
throw new NotImplementedException();
}

public static bool operator ==(DependencyPropertyChangedEventArgs left,
 

DependencyPropertyChangedEventArgs right)
{
throw new NotImplementedException();
}

public override int GetHashCode()
{
throw new NotImplementedException();
}

}
}

DependencyPropertyKey:構(gòu)造函數(shù)傳入該DependencyProperty,然后通過Type來OverrideMetadata,此類只是起到了封裝作用。


namespace System.Windows
{
//構(gòu)造函數(shù)傳入該DependencyProperty,然后通過Type來OverrideMetadata
public sealed class DependencyPropertyKey
{
internal DependencyPropertyKey (DependencyProperty dependencyProperty) {
this.dependencyProperty = dependencyProperty;
}

private DependencyProperty dependencyProperty;
public DependencyProperty DependencyProperty {
get { return dependencyProperty; }
}

public void OverrideMetadata(Type forType, PropertyMetadata typeMetadata)
{
dependencyProperty.OverrideMetadata (forType, typeMetadata, this); }
}
}

  DependencyObjectType:用靜態(tài)Dictionary<Type, DependencyObjectType>來存儲DependencyObjectType,主要有FromSystemType、IsInstanceOfType和IsSubclassOf三個功能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows
{
public class DependencyObjectType
{
//鍵為Type(即OwnerType),值為DependencyObjectType(即ID和systemType)的鍵值對
private static Dictionary<Type, DependencyObjectType> typeMap = new Dictionary<Type, DependencyObjectType>();
private static int current_id;

private int id;
private Type systemType;

//構(gòu)造函數(shù)私有,在FromSystemType里進行構(gòu)造,初始化id和systemType
private DependencyObjectType(int id, Type systemType)
{
this.id = id;
this.systemType = systemType;
}

//基類型的DependencyObjectType
public DependencyObjectType BaseType
{
get { return DependencyObjectType.FromSystemType(systemType.BaseType); }
}

public int Id
{
get { return id; }
}

public string Name
{
get { return systemType.Name; }
}

public Type SystemType
{
get { return systemType; }
}

//用靜態(tài)Dictionary<Type, DependencyObjectType>來存儲DependencyObjectType
public static DependencyObjectType FromSystemType(Type systemType)
{
if (typeMap.ContainsKey(systemType))
return typeMap[systemType];

DependencyObjectType dot;

typeMap[systemType] = dot = new DependencyObjectType(current_id++, systemType);

return dot;
}

//是否是該DependencyObject的子類實例
public bool IsInstanceOfType(DependencyObject d)
{
return systemType.IsInstanceOfType(d);
}

//該DependencyObjectType是否是傳入DependencyObjectType的子實例
public bool IsSubclassOf(DependencyObjectType dependencyObjectType)
{
return systemType.IsSubclassOf(dependencyObjectType.SystemType);
}

public override int GetHashCode()
{
throw new NotImplementedException();
}
}
}
76:

十四. 回歸并統(tǒng)計覆蓋率

  在上面的開發(fā)過程中,我們會不斷的運行和查看代碼通過情況,最后我們也來看一下測試用例的總體通過情況,其實在前面已經(jīng)運行過很多次了,因為每個功能都要經(jīng)過”測試代碼-功能代碼-測試-重構(gòu)“等步驟。

 

  最后也看一下代碼測試覆蓋率,代碼測試覆蓋率對一個系統(tǒng)或者產(chǎn)品來說是一個比較重要的質(zhì)量指標(biāo),可以通過它看出系統(tǒng)的穩(wěn)定性和可控性。一般在項目的開發(fā)中,我們都會以85%~90%的測試代碼覆蓋率作為達標(biāo)的參考標(biāo)準(zhǔn)。

 

  由于MONO本身對依賴屬性沒有那么健全,我們也沒有寫那么詳細的測試代碼,中間直接就實現(xiàn)了一些功能,嚴(yán)格地說,所以本文并沒有完全遵從正規(guī)的測試驅(qū)動開發(fā)流程。

十五. 簡單驗證依賴屬性系統(tǒng)

其實通過上面的測試用例,基本就用不著再單獨測試了,但鑒于覆蓋率比較低的問題,所以最后我們還是來測試一下剛才構(gòu)建的依賴屬性系統(tǒng):

class Program
{
static void Main(string[] args)
{
SimpleDPClass sDPClass = new SimpleDPClass();
sDPClass.SimpleDP = 8;
Console.ReadLine();
}
}

public class SimpleDPClass : DependencyObject
{
public static readonly DependencyProperty SimpleDPProperty =
DependencyProperty.Register("SimpleDP", typeof(double), typeof(SimpleDPClass),
new PropertyMetadata((double)0.0,

new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(CoerceValue)),
new ValidateValueCallback(IsValidValue));

public double SimpleDP
{
get { return (double)GetValue(SimpleDPProperty); }
set { SetValue(SimpleDPProperty, value); }
}

private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("當(dāng)值改變時,我們可以做的一些操作,具體可以在這里定義: {0}", e.NewValue);
}

private static object CoerceValue(DependencyObject d, object value)
{
Console.WriteLine("對值進行限定,強制值: {0}", value);
return value;
}

private static bool IsValidValue(object value)
{
Console.WriteLine("驗證值是否通過,如果返回True表示驗證通過,否則會以異常的形式暴露: {0}", value);
return true;
}

}

測試結(jié)果:

 

到處為止,我們這篇文章也宣告結(jié)束。

十六. 本文總結(jié)

  本篇承接上一篇的寫作風(fēng)格,對上篇模擬一個WPF依賴屬性的實現(xiàn)重現(xiàn)演繹了一遍,上篇是根據(jù)微軟WPF的BCL源碼剖析的,所以這篇我們就詳細的研究一下.NET的跨平臺版本MONO關(guān)于依賴屬性系統(tǒng)的實現(xiàn)。在這篇文章中,我只是起到了剖析源碼的作用,就像研究微軟的BCL一樣,不過MONO的代碼遠沒有微軟的BCL那么龐大,所以研究和復(fù)原起來不是很吃力。如果大家還想繼續(xù)深入,可以去下載相關(guān)源碼,也希望大家和我一起交流探討。

十七. 相關(guān)代碼下載

  在文章的最后,和往常一樣,我們提供代碼的下載,再次溫馨提示:這幾篇文章最重要的就是下載代碼來細細研究,代碼里面也添加了比較詳細的注釋,如果大家有什么問題,也可以直接和我聯(lián)系,如果有不正確的地方也希望多多海涵并能給我及時反饋,我將感激不盡!

 
上圖就是整個代碼包的結(jié)構(gòu)圖


標(biāo)簽:

本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn

文章轉(zhuǎn)載自:博客轉(zhuǎn)載自圣殿騎士

為你推薦

  • 推薦視頻
  • 推薦活動
  • 推薦產(chǎn)品
  • 推薦文章
  • 慧都慧問
掃碼咨詢


添加微信 立即咨詢

電話咨詢

客服熱線
023-68661681

TOP
主站蜘蛛池模板: 成人国产观看 | 日韩av一区二区区 | 三级片网站视频 | 性欧美69| 成人一区二区三区在线 | 日韩国va在线视频 | 福利导航在线观看视频 | 狠狠撸在线视频 | 五月丁香免费 | 岛国电影网 | 国产高清精 | 五月婷婷五月 | 超碰人人操人人操 | 精品人妻一区二区 | 日韩系列免费精品 | 麻豆一区 | 日韩亚洲欧美在线观看 | 亚洲精品偷拍 | 午夜影院老线观 | 国产在线三 | 日韩淫院 | 深夜福利视频在线播放 | 美女黄色网 | 国产精品白丝 | 国产福利tv| 伪娘精品视频专区 | 在线不卡无码 | 国产高清视频在线播放 | 在线aa| 国产乱码| 玖玖爱免费在线 | 国产又黄又大又粗 | 无码专区一| 三级日本午夜在线观看 | 国产精品视频自拍 | 国产女主播一区二区 | 91视操| 国产精品后 | 日韩免费福利影院 | 日韩人人全干 | 婷婷激情综合网 | 东京热在线网站 | 成人大全在线观看 | 亚洲精品传媒 | 自拍偷拍在线播放 | 涩涩在线观看视频 | 久久日本道 | 日韩欧美一区二区三 | 久久综合中文网 | 中文乱伦一区 | 国产爆初菊哭了 | 欧美性爱动态 | 国产大全今日最新 | 精品一曲二曲日韩 | 精品午夜福利 | 日韩二区三区在线观看 | 久久不卡影院 | 国产乱子仑 | 人人人爽 | 日韩成人一级视频 | 日韩精品午夜视频 | 日韩精品伦理 | 久草青草 | 日韩成人在线视频 | 国产70老熟女重 | 成人午夜在线 | 国产又大又黄 | 成人三级的片在线播放 | 日韩老司机免费午夜 | 日韩国产欧美在线一区 | 老湿机网| 午夜涩涩网 | 成人网站三级片 | 福利姬视频在线观看 | 老湿机在线视频 | 天堂毛片在线观看 | 午夜美女影院 | 日韩在线精品蜜柚影院 | 日韩免费v片在线观看 | 深夜福利视频网址 | 超碰成人人人操 | 国产aⅴ激情 | 三级片在线观看视频 | 午夜啪啪视频 | 日韩AV在线免费观看 | 国产a高清 | 91人人干| 精品美女视频 | 国产黄色片 | 日韩国产精品中文 | 日韩女同 | 国产精品一区二区久久 | 日韩国产在线成人 | 黃色A免費看 | 欧美性爱第八页 | 丝袜中出 | 亚洲国产成人在线观看 | 人人澡人人看 | 午夜永久 | 午夜拍国产精品 | 亚洲免费国产精品 | 韩日av一区二区 | 久久中文网| 天天操天天操 | 国产日产精品 | 国产极品网站 | 成人97超碰| 久久中文骚妇射 | 欧洲国产精品 | 无码三级网站 | 日韩激情图 | 草草孚力医院 | 日韩国产 | 欧美精品免费观看二区 | 日韩在线播放视频 | 99久久99| 欧美人牲 | 都市激情亚洲 | 日韩欧美| 成人欧美视频在线观看 | 日韩在线欧美精品 | 午夜视频日韩 | 国产黄色网 | 一区不卡在线观看 | 日韩a级电影| 中文字幕精品视频在线 | 日韩欧美电影 | 国产第-页| 日韩精品二区三区 | 国产乱伦视频网站 | 成人精品在线观看 | 国产91免费视频 | 日韩欧美国产偷亚 | 日韩成a人在线观看 | 三级亚洲精品影视 | 成人网站在线观看视频 | 国产主播网 | 日韩亚洲欧美中文三级 | 午夜福利院 | 在线免费观看污网站 | 玖草视频在线观看 | 黑丝在线麻豆 | 这里只有精品2 | 91丨露脸丨熟女抽搐 | 国产又粗又猛又黄视频 | 日韩亚洲欧美理论片 | 成人污视频网站 | 日韩精品 在线视频 | 囯产永久福利人人看 | 国产三级在线观看网址 | 日韩精品第三页 | 国产嫖妓在线视频 | 日韩在线一区二区 | 色五月婷婷激情网 | 最新国产精品 | 本道在线视频 | 国产sm重口 | 激情图片区故事区 | 欧美成在线| 一区二区视频 | 成人午夜激情视频 | 日韩综艺大全 | 国产精品女同一 | 日韩乱轮综合网 | 成人免费a片sod| 日屄免费视频 | 欧美视频在线不卡 | 精品视频一区二区 | 亚洲精品字幕在线观看 | 国产乱人 | 亚洲激情成人小说 | 午夜视频大全 | 日韩在线视频在线观看 | 天天操天天射天天爽 | 国产三级片视频 | 国外成人在线视频 | 日韩乱轮综合网 | 国产精品午夜视频 | 日韩理伦片 | 国产cd视频在线播放 | 日韩欧美亚洲国产精品 | 日韩熟妇 | 日韩一区无码 | 免着一級a一片 | 国产乱人伦无码视频 | 久久影视综合2o23 | 91视频管网 | 九九热九九 | 男女网站在线观看 | 国产成人高清 | 国产青青草 | 美女爱爱网 | 国产高潮在线观看 | 国产色无码精品 | 三级片国产精品 | 国产哟在线观看 | 在线天堂最新版资源 | 午夜在线成人 | 三级乱伦亚洲影视 | 国产精品高潮呻吟久久 | 欧美精品一二三区 | 玖玖电影网 | 丁香五月婷婷综合网 | 日韩精品a | 日韩手机视频 | 成人黄瓜视频 | 成人小蝌蚪www | 三级网站黄色 | 国产福利一区二区三区 | 欧美性爱视频网站 | 国产成年人网站 | 午夜影视影院 | 国产乱在线 | 国产午夜福利 | 极品尤物丰满暴露尤物 | 成人精品综合免费视频 | 黑人巨茎大战欧美白妇 | 岛国大片| 日韩欧美一区二区三 | 国产屁屁影院 | 中文字幕人 | 97视频久久久 | www.日日| 女人脱精光按摩AA片 | 日韩伦理电影免费观看 | 日韩欧美亚洲片 | 日韩大香蕉国产 | 激情小说图片区 | 精品久久不卡 | 黄色一级免费观看 | 经典三级在线 | 在线视频自拍 | 图片区小说区亚洲 | 国产爆乳视频 | 日本乱伦一区 | 日韩午夜场 | 嫖妓自拍播放 | 草玖视频 | 日韩精品欧美一区二区 | 婷婷四房综合激情五月 | 成人AV毛片| 日韩成人极品在线内 | 国产高潮白浆喷水 | 在线激情网 | 日韩欧美伦理 | 欧美性受 | 夜夜操夜夜干 | 精品男女在线观看 | 国产淫网 | 丁香五月在线观看 | 麻豆精品在线观看 | 国产高清视频在线 | 自拍偷拍第1页 | 成人国产片女人 | 日韩一区二区三区激情 | 91视频软件| 免费黄色av网址 | 午夜国产片| 久久天堂 | 成人高清字幕在线播放 | 欧美性爱xxx | 日韩精品首页 | 日韩在线观看一区二区 | 91网入口| 激情小说图片亚洲 | 日韩精品亚洲精品 | 激情五月天成人 | 日韩成人免费 | 国产高清三级 | 老湿机福利视频 | 日韩在线播放欧美字幕 | 日韩在线一区 | 91超碰人人 | 日韩伦理片大全 | 国产精品国产自 | AV日韩精品| 日韩高清片 | 国产丝袜熟女 | 天堂com| 黄色三级片网站 | 国产高清无码在线观看 | 九九九成人 | 午夜视频app | 国产一区二区网站 | 三级精品手机在线 | 激情文学图片区 | 三级视频网 | 色情性黄片免费 | 老湿机视频在线观看 | 五月天婷婷丁香网 | 97伊人网 | 成年免费A级毛片 | 日韩国产欧美综合网 | 国产在线无码 | 五月婷婷影院 | 日韩精品免费在线观 | 日韩中文字幕在线视 | 成人免费视频播放 | 91幅利视频 | 午夜成人黄色电影 | 久久这里是精品 | 三级片在线播放视频 | 欧美成人a | 国产屁屁 | 丁香五月亚洲 | 91影视网| 日韩高清中文字幕 | 岛国av免费| 日韩丝袜电影 | 国产ts视频0 | 91视频高清 | 国产91呆| 日韩桃色激情综合 | 国产亚洲福利 | 夜夜干夜夜爽 | 91视频免费在线观看 | 国产AV久久| 日韩中文字幕不卡 | 成人黄页 | 成人福利网站在线观看 | 日韩欧洲在线高清一区 | 自拍偷拍免费观看视频 | 日韩综合 | 日韩成人成色 | 久热思思| www.啪| 日韩欧美伦理电影 | 日本草逼网 | 三级视频毛片 | 91香蕉短 | 五月天婷婷综合 | 午夜精品福利视频 | 国产在线无码免费网站 | 日韩中文字幕在线视频 | 在线免费三级 | 日韩小视频网站 | 午夜福利鸡 | 久久中国| AV在线一区二区三区 | 日韩电影院| 国产www三级片视频 国产wwwwxxxx | 国产变态一区二区三区 | 天堂资源在线 | 成人福利夜色影视亚洲 | 免费在线视频一区二区 | 福利姬在线视频 | 三级在线观看大全免费 | 日韩精品视频免费网址 | 成人免费精品 | 天天日天天射天天爽 | 亚洲S色| 久草视频福利 | 91AV在线视频观看 | 97在线观看 | 国产91成人在 | 日韩A级| 国产91网址| 涩涩在线观看视频 | 欧美另类人妖 | 日韩AV一二三区 | 国产高清区 | 日韩亚洲欧美中文三级 | 性盈盈影院在线观看 | 丁香五月天论坛 | 成人毛片在线 | 国产又黄又硬又粗 | 人人妻日本 | 丁香六月婷婷五月 | 日韩欧美亚洲国产伊人 | 成人猫咪社区 | 国产精品露脸国 | 日韩性爱视频在 | 日韩电影精品 | 国产成人精品AV | 自拍偷怕第一页 | 亚洲AV无码高潮喷 | 午夜成人福利院 | 亚洲品质在线观看 | 91社区福利 | 日韩在线中文 | 国产精品对白 | 三级免费黄 | 国产又粗又大又黄又爽 | 三级在线a片 | 午夜影视网站 | 国产激情精品一 | 国产成年人 | 日韩国产在线0 | 日本三级黄色网址 | 国产第一艘航母 | 高潮流白浆视频 | 日韩欧美一中文在 | 日韩中文亚洲 | 成人国产欧美日韩在 | 日韩欧美精品有码在线 | 深夜福利视频导航 | 天天插夜夜干 | 日韩性爱视频在线观看 | 午夜影视网站 | 日韩精品在线免费观看 | 国产精品宾馆 | 中文字幕在线99 | 超碰成人人人操 | 日韩电影在线观看视频 | 国产视频高清 | 成人区精品人 | 日韩伦理中文字幕 | 国产免费高清 | 天天干天天干天天干 | 成人国产三级在线 | 午夜成人影院催经视频 | 国产37页| 偷拍第8页 | 日本午夜福利 | av学生妹 | 成人免费片 | 狼人狠狠干| 丁香五月婷婷五月 | 国产va| 偷偷撸影院 | 成人三级在线观看视频 | 91视频爱拍 | 女主播在线视频 | 岛国在线观看 | 日本资源网站 | 国产首页| 99中文字幕在线播放 | 无码不卡网 | 国产又粗又大又黄又爽 | 日韩精品真人荷官 | 91视频污 | 国产大神背着在线播放 | 国产午夜免费看 | 日韩新片王网 | 成人三级片在线 | 午夜成人激情 | 国产a精彩 | 国内91视频 | 午夜激情成人影院 | 日韩资源网 | 日本在线不卡一区 | 日韩在线观看高清视频 | 无码高清不卡在线 | 日韩一级在线精品国产 | 日夜精品视频 | 91福利视频网 | 日韩影视传媒在 | 少妇28p | 护士肉欲39系列 | 日韩成人小视频 | 国产ts上海在线观看 | 日韩精品一卡2卡 | 国产精品女同一 | 国模精品一区二区三区 | 日韩激情三区 | 国产成a人亚 | 精品在线观看视频 | 日韩午夜视 | 做爱在线免费观看网站 | 国产精品免费自拍 | 国产又粗又黄的视频 | 日韩在线入口 | 三级片网站国产 | 日韩黄在线 | 国产盗摄一区二区三区 | 日本道久久 | 无码成人午夜电影免费 | 国语自产| 成人免费视频国产免 | 日韩午夜在线免费观看 | 国产一级做受视频 | 国产视频第21页 | 69老司机在线观看 | 日韩中文字幕在线视频 | 成人免费精品一二三区 | 三级片男人天堂 | 日韩欧美国产完整版 | 国产白丝| 亚洲视频三 | 国产白丝在线观看 | 爆操人妖| 色播五月婷婷 | 国产在线无码播放 | 韩国无码无遮挡 | 成人深爱网 | 三级在線日韩中文 | 国产一区二区三区传煤 | 国产三级毛片 | 成人高清网站 | 国产爆乳视频 | 三级AV| 欧美高清精品一区二区 | 三级黄色网址 | 久久网综合网 | 国产极品视频 | 精品成人 | 国产a网 | 玖玖视频免费在线观看 | 午夜三级在线 | 日韩欧美一区二 | 日韩福利电影院 | 日韩精品系列在线 | 日韩精品高清无码 | www.A片| 日韩在线入口 | 国产不卡高清在 | 成人免费a | 日韩欧美精品视频在线 | 日韩二区欧美三区 | 美女视频毛片 | 拍拍拍免费网站 | 顶级深喉口爆系列喉吞 | 黄A网站| 成人午夜短视频 | 日韩伦理片影院 | 日韩在线一二三 | 日韩欧美视频 | 国产l精品 | 老牛影视传媒一区二区 | 日韩无码高清中文字幕 | 三级在线看片 | 成人网址在线观看 | av三级网 | 偷拍自拍在线看 | 日韩欧美二区在线观看 | 丝袜第一页 | 成人免费观看网 | 国产三级精品三级观看 | 日韩精品第五页 | 欧美日韩国产在线观看 | 成人69A片 | 日韩五级片 | 日韩在线视频导航 | 午夜xx | 国产美女视频网站 | 日本xxxx色| 国产片在线观看 | 国产99自拍 | 强奸乱伦首页 | 高潮喷水无码 | 精品国产自| 国产AV淫乱兄妹 | 日逼视频网站 | 国产精品女同一区二区 | 成人毛片在线观看 | 亚洲日韩国产成人另类 | 深夜视频18+在线 | 人妻一区二区三区 | 日韩欧美大片精品黄 | 日韩在线精品蜜柚影院 | 国产免费高清 | 超碰入口| 午夜福利在线观看网站 | 不卡免费视频 | 日韩第一页在线观看 | 亚洲美女影院 | 九九九热精品 | 玖玖视频在线 | 爆乳护士一区二区三区 | 成人午夜免费在线 | 日韩免费在线视频观看 | 亚洲国产精品免费 | www.加勒比| 天天操夜夜爽 | 天堂精品在线 | 国产美女精品 | 一区区视频 | 成人黄色 | 成人午夜国产福利 | 深夜福利精品 | 欧美性爱日韩性爱 | 五月花网站 | 国产91精品入| 尤物视频免费 | 黑人一区二区 | 日韩新片网址 | 国产成年女人在线观看 | 97就去色| 日韩精品一区二区三 | 在线看三级| 日韩亚洲高清中文字幕 | 日本综合在线 | 自拍偷拍第8页 | 日韩无砖专 | 日韩欧美一区在线播放 | 日韩一二三区视频精品 | 激情熟妇 | 日韩在线免 | 午夜成人在线观看视频 | 天堂…在线最新版资源 | 国产AⅤ无码 | 深夜激情福利 | 日韩亚洲人成在 | 日韩精品二区三区不卡 | 九九综合色 | 日韩高清无码专区 | 日韩大片网站 | 一伦一色一性一交一配 | 日韩性爱自拍 | 狠狠干天天操 | 午夜福利理论 | 国产三级片在线看 | 中文有码人妻 | 国产在观| 国产95在| 91小视频在线 | 成人不卡免费观 | 午夜在线无码 | 亚洲偷自 | 国产黑丝在线 | 国产v亚 | 日日夜夜撸视频 | 日韩电影大片 | 日韩无人区码卡二卡 | 男女交配视频网站 | 蜜桃传媒网 | 日韩专区国产在线 | 一区二区三区伦理片 | 国产91精选| 91视频污污污 | 三级理论片 | 91视频在线看 | 国产高清超 | 日韩无人区码卡二卡 | 中文字幕视频99 | 日韩在线aⅴ免费视频 | 日韩欧美小电影 | 国产精品尤物在 | 91手机论坛 | 性国产在线观看 | 日韩成人精品在线观看 | 在线国产三级免费 | 日韩欧美国产动漫制服 | 三级片国产在线 | 狠狠干2019 | 成人免费AAA片 | 97爱爱| 能看的黄色网址 | 91丨露脸丨熟女精品 | 三级黄色网址 | 成人超碰97 | 九一看片 | 国产精诚人品 | 超碰网97| 亚洲国产高清精品 | 老湿机免费在线观看 | 日韩美女网站 | 三级中文在线 | 国产第一页福利 | 在线综合自拍 | 成人精品一区二区无码 | 日韩精品影片 | 日韩网友自拍区 | 狠狠操天天操 | 日韩女同视频 | 日韩成人大片在线观看 | 日韩不卡一区 | 成人免费视频国产免 | 日韩电影在线观看视频 | 人人操人人超碰 | 日韩欧美国产激情视频 | 日韩伦理在线看网站 | 国产sm精品调 | 日韩精品视频在线播放 | www天堂在线观看 | 日韩欧美视频一区二区 | 日韩h片 | 国产69精品久 | 日韩综合一区 | 国产性爱免费观看 | 精品九九九 | 做爱在线免费观看网站 | 日韩视频亚 | 99中文字幕网 | 国产大片视频免费观看 | 午夜视频hd | 国产传媒剧情在线观看 | 日韩福利在线视频播放 | 日本天堂中文字幕 | 日韩午夜网站 | 国产三级在线观看网址 | 人妖视频网址 | 日韩高清伦理 | 精品日韩一区二区三区 | 三级黄色网在线观看 | 国产初高中生在 | 国产优物在线观看 | 国产免费中文 | 午夜成人视频免费看 | 日韩欧美亚洲妖精 | 欧美另类高清 | 国产三级片在线二区 | 国产老女人网址 | 日韩成人免费体验 | 国产va在线观看 | 91豆奶| 午夜成人高清无码 | 天天干天天日天天射 | 中文字幕日本有码 | 尤物网站在线 | 日韩精品淫途 | 成人毛片AV无码 | 日韩欧美成人影院 | 日韩免费高清专区 | 男人的天堂黄色 | 国产成年网 | 91丝瓜app| 三级视频婷婷麻 | 久久这里只有精品66 | 国产超薄肉色丝袜网站 | 国产不卡一 | 毛片中文字幕 | 国产无码在线播 | 国产精品9 | 日韩午夜视频在线观看 | 高清无码一卡二卡 | 日韩综合 | 成人国产一区二区三区 | xxx.国产| 国产精品成人在线 | 亚洲精品深夜福利 | 欧美成人在 | 97日韩电影 | 在线一区二区视频 | 97免费看| 午夜影院男女 | 黄射视频| 涩涩在线视频 | 日韩精品免费在线观看 | 97伦理片| 亚洲色悠悠 | 国产在线三级视频观看 | 国产嫖妓一区二区三区 | 国产色情一区二区三区 | 国产免费播放器 | 日韩一区二精品成人免 | 国产精品久久久一区 | 国产福利片在线 | 自拍偷拍第一页 | 国产成人啪精品 | 亚洲国产精品色色 | 成人免费三及片 | 久久综合久久网 | 国产麻豆久久 | 性欧美长视频 | 午夜在线一区二区三区 | 成人午夜免费影院 | 国产成年视 | www.日本xxx| 自拍偷拍第八页 | 在线天堂最新版资源 | 日韩精品首页 | 婷婷五月激情视频 | 国产盗摄精品 | 91肥熟国产老肥熟女 | 日韩剧完整 | 午夜无码在线观看视频 | 亚洲精品岁国产精品 | 成人免费精品一二三区 | 视频一区二区欧美 | 国产亚洲无码在线 | 日韩亚洲中文字幕另类 | 午夜成人福利免费 | 日韩国产欧美在线 | 天美mv传媒 | 日韩日韩日韩日韩 | 欧美另类性爱 | 足交网站在线观看 | 97色视| 中文字幕在线观看不卡 | 日韩精品二区三区不卡 | 精品偷拍自拍 | 丁香婷婷五月 | 日韩欧美无砖专区 | 国产成人三级 | 日韩欧美另类一区在线 | 国产在线观看免费无码 | 日韩大片中文 | 日韩视频在线播放 | 日韩区一区二区三区四 | 日韩欧美亚洲0 | 日韩精品乱 | 天堂网毛片视频 | 日韩精品日 | 乱伦露脸 | 福利微拍 | 国内外成人在线 | 国产97视 | 天堂网最新网址 | 日韩欧美在线综合 | 午夜电影全集 | 午夜成人视频在线观看 | 97在线看 | 成人伦理影院 | 天美免费在线传煤mv | 天堂…在线最新版资源 | 午夜视频播放器 | 成人小说图片视频 | 日韩中文字幕高清一区 | 亚洲国产色色 | 日韩一级免费免费视频 | 欧美激情日韩国产绯色 | 综合久久中文 | 成人午夜视屏 | 日韩三夜精品在线播放 | 日韩欧美激情兽交 | 成人午夜视频精品一区 | 午夜成人激情影院 | 天堂网在线看 | 国产精品福利 | 色婷婷AV| 日韩综合在线观看 | 国产又粗又猛又黄视频 | 午夜福利1000 | 日韩电影影院 | 午夜成人影院 | 日韩在线高清视频蜜桃 | 东方AV在线免费观看 | 91精品免费 | 日韩精品中文字幕 | 顶级深喉口爆系列喉吞 | 国产精品一曲 | 午夜免费影院 | 日韩中文字幕乱伦 | 人人妻人人操人人爽 | 国产又粗又硬又长又爽 | 无码三级在线 | 日韩亚洲欧美专区 | 变态sm天| 日韩免费一二三四区 | 午夜在线电影 | 97韩剧网| 91视频3p| 中文字幕久久网 | 91网站 | 国产拳交在线 | 国产做爰高潮呻吟视频 | 日韩成人三级在线观看 | 国模冰冰炮图 | 韩日精品视频 | 精品熟妇 | 做爱在线免费观看网站 | 日韩亚洲综合精品国产 | 国产精品成人高清 | 国产91最新欧 | 国产三级片在线视频 | 中国操逼网站 | 成年人午夜福利 | 这里只有精品66 | 日韩精品在线 | 国产v精品成人免 | 夜福利视频观看视频 | 在线视频一二区 | 国产午夜高清无 | 91视频网在线 | 玖草在线免费观看 | 日韩欧美亚洲精品在线 | 亚洲福利网| 学生妹A片| 亚洲另类图片小说网站 | 伦乱熟女 | 日韩无码视频网 | 91一区视频| 91直播视频| 超碰www| 成人黃色A片免费 | 可以看A片的网址 | 国产丝袜美女一 | 亚洲特黄 | 日韩三级电影 | 麻花原创mv免费观看 | 怡红院一区二区三区 | 人妖另类影院 | 夜福利视频 | 日韩福利片一区二区 | 成人国产日本亚洲精品 | www.国产网站 | 成人免费午夜在线观看 | 日韩国产欧美制服 | 日韩午夜专区 | 激情文学综合网 | 日韩欧美精品 | 日韩午夜福利电影 | 午夜视频免费版 | 午夜成人精品免费看 | 国产三级国产三级国产 | 意大利熟女复古毛茸茸 | 午夜在线视频 | 成人免费A片白浆 | 秋霞日韩 | 91手机视频 | 深夜福利小网站 | 国产精品一曲二曲 | 美女三级片网站 | 91网在线视频 | 午夜影视界 | 韩日高清视频 | 丁香五月天综合网 | 每日国产福利 | 日韩午夜一区 | 成人高清字幕在线播放 | 欧美乱伦视频 | 97色色资源网 | 福利一区视频在线观看 | 国产嫖妓一区二区三区 | 日产又大又黄又爽又猛 | 欧美成人免费观看 | 日韩亚洲精品影院 | 自拍偷拍精品视频 | 日韩精品第一区 | 深夜福利资源 | 日韩精品亚洲电影天堂 | 中文字幕丝袜 | 日韩欧美电影 | 夜福利导航 | 成人三级伦理片 | 亚洲亚洲人成综合网络 | 国产三级网址 | 国产91福利电影在线 | 国产兄妹在乱搞 | 成人午夜在线免费观看 | 高清无码免费 | 婷婷五月激情综合网 | 日韩亚洲欧美中文高清 | 成人三级影视 | 日韩教师另类自拍 | 三级a黄 | 日韩欧美高清 | 午夜精品在线观看 | 日韩伦理一区二区三区 | 久久视频中文字幕 | 国产无码在线网站 | 成人永久免费视频 | 日本激情小说视频 | 福利在线观看入口 | 国产成人久久久久久久 | 爱豆免费在线看 | 午夜成人电影院 | 午夜精品成人无码 | 这里都是精品 | 超碰97在线免费观看 | 蜜桃传媒网| 久热青草 | 成人午夜福利视频在线 | 亚洲卡一卡二卡三 | 午夜成人小视频 | 日韩国产中文综合网 | 国产大秀视频 | 国产女主播在线视频 | 国产偷自拍 | 老湿机在线观看 | 日韩在线观看三区 | 欧美日韩在线不卡 | igao视频在线| 成人短视频在线播放 | 午夜伦理片在线 | 国产在线观看啊 | 深夜福利网站欧美 | 日韩在线视频第一页 | 国产成人无码精品亚洲 | 日韩一区二区三区免费 | 婷婷狠狠干 | 激情少说视频在线播放 | 三级黄色网页 | 久久足交 | 网站成人高清视频 | 色网综合| 午夜免费激情 | 国产精品自拍第一页 | 成人免费午夜影院 | 四虎新地址 | 午夜成人在线播放 | 亚洲激情小说网 | 成人免费AAA片 | 日韩欧美国产中文综合 | 国产精品鲁一鲁 | 日本中文字幕在线观看 | 日韩美女视频在线播放 | 麻豆破解网站 | 精品精免费 | 日韩欧美在线中文字幕 | 国产成年一级电影 | 性盈盈影院在线观看 | 无码人妻又粗又大 | 海角亂倫精品一区二区 | 偷拍自拍视频在线观看 | 老A成人无码影院 | 丁香五月亚洲婷婷 | 国产又大又黄视频 | 国产成人精品在线观看 | 午夜福利剧场 | 足交网站在线观看 | 黄色三级网站视频 | www.东京热| 福利视频深夜 | 狠狠操亚洲 | 热99精品| 日韩欧美h | 男女交配视频网站 | 一区二区三区乱伦 | 精品国产乱码久久 | 在线播放一区 | 精品人妻中文字幕 | 日韩在线视频一区二区 | 玖玖爱视频在线 | 日韩在线精品视频播放 | 激情文学久久 | 夜福利视频观看视频 | 玖玖爱中文字幕 | 免费v片 | 日韩国产专区 | 日韩在线免费看 | 欧美性爱在线视频 | 日韩精品成人一 | 日韩艹逼网站 | 午夜在线伦理 | 日韩美一区二区 | 丁香五月婷婷网 | 日韩一级中文字幕在线 | 日韩灭亚洲精品 | 成人国内精品 | 三级在线网址 | 天堂在线资源网 | 日韩AV电影一区二区 | 日韩无人区 | 成人欧美视频在线观看 | 中文久久网 | 日韩免费影院 | 成人午夜福| 国产高清无码不卡 | 成人午夜视频在线播放 | 国产无码电影 | 麻豆传媒一区 | 午夜伦理片 | 国产精品永久成人免费 | 91肥熟国产老肥熟女 | 激情文学之图片区 | 性欧美长视频 | 国产成年| 国产精品免费一区 | 色网站在线 | 成人国产一区 | 国产一级a | a级视频在线观看 | 国产尤物 | 日韩精品专区线上观看 | 日韩在线高清视频 | 免费久草| 日韩精品色| 三级在线网站 | 福利直播导航在线观看 | 成人va在线| 国产第一页草草 | 日韩美女在线视频 | 欧美日韩精品喷水 | 日韩短剧tv | 国产8区 | 色哟哟一中文字幕 | 自拍视频区| 成人影院中文字幕 | 日韩国产v片一区二区 | 国产+中文 | 欧美性爱网站大全 | 精品视频在线观看 | 日韩一区二区三区四区 | 日韩一进一出免费试频 | 日韩亚洲欧美中文三级 | 午夜在线一区二区三区 | 国产三级片观看 | 日韩欧美亚洲免费在线 | 精品一区欧美 | 欧美成人免费观看视频 | 国产va免费观看 | 国产91免费在线观看 | 城中村嫖妓在线观看 | 成人一区二区不卡在线 | 日韩午夜理论片中 | 午夜伦理视频 | 久久综合视频网 | 国产乱伦区 | 亚洲色老头 | 国产a在线不卡 | www.成人午夜 | 三级在线观看大全免费 | 国产白丝jk| 国产又黄又大 | 午夜色色影院 | 日韩二区三区无 | 九九九热精品 | 激情图区视频 | 天堂网在线观看视频 | 国模大胆一区二区三区 | 一期二期三期视频 | 福利电影网 | 国产爆乳在线观看 | 动漫一区二区 | 国产哟在线观看 | 成人三级网 | 欧美在线不卡视频 | 午夜成人影视频道 | 日韩一级一区二区不 | 日韩欧美综合激情专区 | 亚洲精品在线国产 | 激情小说亚洲 | 一区二区蜜桃臀 | 国产三级三级三级 | 国产精品夜夜爽 | 国产一曲二曲三曲 | 日韩综合欧美 | 天天干狠狠| 尹人国产| 福利视频导航在线观看 | 美乳一区| 日韩熟肥穴 | 三级中文在线 | 91视频综合| 99这里只有精品 | 性交网站在线观看 | 日韩动漫一区二 | 成人午夜色情无码精品 | 成人欧美精品区二区三 | 亚洲精品影视 | 日韩国产精品综合免费 | 国产大学生一区 | 午夜国产片 | 日韩在线看 | 国产伦理片网站 | 国产精品自在线 | 国产三级在线观看免费 | 日韩女同精品一区二 | 精品一区二区三区人妻 | 日韩高清无码专区 | 深夜福利视频在线播放 | 成人情趣用品 | 天天操穴 | 亚洲日本中文字幕 | 岛国大片免费看 | 东方成人| 成人超碰97 | 日韩理论电影网 | 午夜成人无码 | 91洮色| 国产91区| 狠狠撸视频 | 国产91丝袜在线播放 | 九七精品| 国产精品被艹 | 99资源站 | 三级国产 | 日韩福利社 | 国产人妖一区 | 福利姬免费视频 | 日韩综合鲁一 | 成人精品福利午夜无码 | 白浆在线| 国产精品偷伦 | 国产乱人 | 伦片丰满丰满午夜电影 | 日韩欧美在线免费看 | 东方AV在线观看 | 日韩在线亚字幕精品 | 日韩欧美st | 成人免费观看在线看 | 在线能看的黄色网址 | 国产丝袜在线 | 一级做受 | 午夜福利导航视频 | 国产性爱免费观看 | 日韩免费电影 | 日韩精品视频 | 成人午夜福利片 | 日韩国产欧 | 精品免费一区 | 国产大全入 | 卡一卡二中文字幕 | 国产精品黑色丝 | 三级片在线观看视频 | 日韩精品在线播放视频 | 国产激情视 | 激情图片另类小说 | 国产精品九九九 | 五月婷婷六月丁香 | 欧美一区二区三区视频 | 成人午夜电| 国摸冰冰 | 天天天天干| 成人AV毛片 | 无码在线影院 | 亚洲偷怕自拍 | 国产成视频在线观看 | 精品动漫一区 | 玖玖免费在线视频 | 国产成人高清 | 国产欧美日韩 | 中文字幕丝袜在线 | 东京热综合网 | 国产又黄又大又粗 | 国产欧美日韩 | 欧美午夜视频 | 国产精品一二三 | 自拍一页| 国产精久久一区 | 岛国无码av | 亚日韩精品| 国产a级 | 国产91欧美 | 日韩精品视频免费 | 日韩综合在线一区二区 | 美女三级网站 | 日韩欧美国产动漫一区 | 极品唯美女同互摸互慰 | 免费三级在线 | 日韩国产精品一 | 深夜福利视频在线观看 | 人妖操伪娘 | 人妖免费网站 | 91伊人久久 | 日韩欧美网| 一区二区白丝 | 人人操97| 天天操天天爽天天干 | 肏屄三级视频 | 日韩热映专区视频合集 | 成人免费ā片在线观看 | 三级免费黄 | 成人精品伪娘 | 日韩一区在线免费观看 | 成人免费毛片片v | 精品乱伦一区二区三区 | 三级中文字幕免费 | 日本一二三不卡 | 免费福利小视频 | 日韩丝袜中文字幕 | 国产精品成人片 | 成人午夜在线播放 | 日韩亚洲欧洲中文版 | 福利精品| 另类一区| 日日操日日操 | 日韩专区一区二区 | 中文字幕第27页 | 午夜成人影院 | 日韩精品三级一区二区 | 国产成在人线在线播放 | 日韩中文字幕欧美视频 | 黄片内射 | 婷丁五月| 成人午夜在线免费 | 日韩欧美在线综合网 | 无码成A毛片免费 | 国产白丝网站 | 丁香五月在线观看 | 国产老熟女网站 | 日韩精品a | 日韩无砖专区体验区 | 四虎私人影院 | 综合五月 | 日韩高清无码专区 | 成人黄片免费看 | 日韩视频在 | 97素材| 午夜福利免费院 | 三级中文亚洲精品字幕 | 意大利熟女复古毛茸茸 | 成人国产综 | 狠狠操夜夜操天天操 | 91热爆| 国产日产欧产 | 国产是什么意思 | 国产成人网站在线观看 | 国产视频第二页 | 日韩av片在线 | 国产无码精品合集 | 欧美自拍偷拍 | 五月天成人影院 | 欧美成人免费网站 | 午夜影视在线 | 国产黄a三级三 | 手机超碰干| 成人影片麻 | 日韩欧美在线综合网 | 深夜福利视频在线 | 国产三级免费观看 | 女同变态另类 | 欧美日韩精品一区二区 | 狠狠干狠狠插 | 性做久久久久久久久久 | 午夜伦理电影院 | 日韩淫水 | 国产我不卡 | 国产精品剧情一区 | 91在线电影| 国产又粗又大又爽又黄 | 日韩精品久久 | 欧美性爱加勒比 | 激情视频小说网 | 强奸乱伦视频网址 | 国产字幕| 激情小说视频网 | 日本中文字幕无码 | 97人人操人人干 | 国产夫妻片 | 日韩高清在线观看视频 | 中文字幕免费毛片 | 资源天堂在线 | 成人导航在线观看 | 日韩中文字幕在线有码 | 国产l精品国产亚洲 | 欧美大B| 天天日狠狠操 | 天天艹夜夜艹 | 成人精品v视频在线 | 视频一区二区在线播放 | 国产主播福利在线 | 九一九色 | 国产精品免费大 | 日韩精品欧美一区二区 | 日韩午夜理论免费网站 | 国产自拍偷拍区 | 国产电影三级在线观看 | 色色色色综合 | 日韩激情免费观看大片 | 一区二区视频在线观看 | 中文字幕亚洲有码 | 在线观看亚洲国产精品 | 精品国产乱码一区二 | 激情五月婷婷综合网 | 日韩国产 | 深夜福利一区 | 国产盗摄-老牛影视 | 欧美在线播放视频三区 | 日韩在线直播 | 国内自产自拍 | 国内自拍一区 | 国产αv无 | 深夜小福利 | 国产精品久线在线观看 | 国产片黄| 日韩国产激情在线 | 日韩国产亚州欧美 | 日韩三级片网站 | 色图视频 | 在线第一页 | 欧美另类性 | 午夜在线视频网 | 午夜福利成人在线 | 中国三级片那里看 | 国产熟女麻豆 | 欧美另类高清 | 福利二区 | 国产精品理论片 | 三级视频网站在线观看 | 午夜私人影院 | 国产福利姬 | 日韩系列3 | 国产久久一区 | 成人午夜AV在线 | 色悠悠,综合 | 三级黄在线播放 | 玖玖热在线视频 | 日韩一级二级 | 最新av网页| 国产在线无码免费网站 | 成人性夜 | 在线观看午夜福利 | 国产jk| 久草国产在线视频 | 成人动漫在线观看 | 玖玖视频免费观看 | 蜜桃成人无码 | 日韩在线精 | 日韩制服国产精品一区 | 国产91这里都是精品 | 自拍偷拍在线视频 | 国语对白自拍 | 成人看片网站 | 日韩电影网新片 | 久久不卡在线 | 孕妇三级片在线观看 | 日韩精品免费在线视频 | 日本伦理一区二区 | 亚洲国产高清国产精品 | 悠悠色综合 | 日韩精品极品视 | 国产人妖在线免费观看 | 久草福利视频 | 成人永久免费视频 | 国产亚洲高清在线 | 欧美另类综合网 | 日韩欧美午夜电影 | 午夜福到 | 婷婷丁香五月亚洲 | 午夜成人精品在线观看 | 天天爽爽夜夜爽爽 | 久这里只有精品 | 成人亚洲性情网 | 国产精品黑色 | heyzo东京热| 老牛影视国产精品 | 自拍偷拍第七页 | 91自拍最新| 玖玖爱亚洲 | 日本www在线观看 | 亚洲图片另类小说 | 自拍在线观看 | 99偷拍| 老司机精品导航 | 国产不卡1 | 国产免费三级片完整版 | 91污在线观看 | 日韩精品二区页 | 亚洲视频三 | 在线黄色AV网站 | 日韩有码欧美激情 | 午夜成人免费 | 人伦无码 | 深夜性爱福利 | 成人一区二区三区在线 | 日韩v国产v| 老色在线| 午夜视频官网 | 国产又大又粗又爽视频 | 欧美另类xxx | 日韩电影在线电影 | 国产无码高清在线 | 网友自拍偷拍第一页 | 日韩欧美中文字幕免费 | 在线无码小电影 | 日本不卡三区 | 国产成人亚洲 | 午夜精品视频在线观看 | 深夜成人福利在线观看 | 一级做受视频 | 日韩欧美精品免费观看 | 国产精品国产精品国产 | 国产99视频在线观看 | 国产xxxx | 成人看片在线观看免费 | 人妻奶水| 中文字幕-色哟哟 | 三级高清在线观看 | 91网首页| 久久国产影院 | 日韩动漫一区二区 | 国产乱伦区 | 午夜寂寞视频 | 美女网站全黄 | 亚洲国产精品成人网站 | 成人午夜高清无码 | 日韩中文有码 | 日韩在线看视频 | 国产a网| 日韩精品视频在线观看 | 国产99在线播放 | 午夜福利精品在线观看 | 成人爽爽婬人 | 成人开心网| 男人的天堂狠狠干 | 人人看97 | 91插逼| 老司机69 | 日韩大片中文 | 日韩国产综合在线 | 日韩精品在线二区三区 | 夜色福利视频导航 | 草莓视频在线 | 日韩另类国产 | 日韩三级片名 | 欧美成人在线视频 | 日韩一区二区三区免费 | 天堂在线精品 | 国产精品一曲二曲 | 另类女同 | 97色蜜桃网| 麻豆国产在线 | 在线天堂最新版资源 | 白丝一区 | 深夜欧美 | 日韩娇小XXXⅹHD | 国产无码影院 | 亚洲五月天婷婷丁香 | 午夜手机在线视频 | 97涩涩的网站 | 日韩激情网| 波多野结喷水 | 玉足足交网站 | 五月婷婷网 | 日韩逼网| 99自拍视频在线观看 | 日韩小网 | 日韩国产二区不卡在线 | 国产拍拍 | 午夜无码福 | 人妖视频网址 | 国产亚洲/无码精品 | 国产成人不卡在线 | 日韩一区二区三区四区 | 精东影业秘国产传媒 | 深夜电影免费在线看 | 成人自拍 | 国产三级在线观看视频 | 国产成人不卡 | 日韩精品在线第二页 | 午夜不卡视频在线观看 | 欧美不卡在线 | 亚洲7777| 国产在线免费看 | 尤物视频免费观看 | 日韩制服丝袜在线观看 | 国产主播福利在线观看 | 国产是什么意思 | 天天爽夜夜爽 | 麻花传剧MV高清资源 | 乱伦五月天 | 日韩视频中文字幕 | 玖玖视频免费在线观看 | 日韩视频一区二区 | 自拍偷拍第九页 | 国产激情专区 | 国产色色网 | 国产色综合久 | 97碰91| 欧美黄色性爱网站 | 麻豆传媒在线播放 | 国产三级在线免费播放 | 日韩好片一区二 | 三级精品视频 | 日韩射吧 | 又粗又大又黄又爽 | 日韩高清在线一 | 黄色毛片三级 | 国产成人三级在线观看 | 午夜天堂影院 | 国产又黄又猛 | 欧美性爱www| 亚洲国产成人综合色 | 成人三及片 | 三级成人国产 | 成人论坛网址 | 日韩在线第二页 | 偷偷撸影院 | 夜色帮福利网 | 97社区资源网 | 福利姬视频观看 | 午夜福利在线观看网站 | 中文一级毛片HD网站 | 中文字幕蜜桃 | 日韩精品在线观看免费 | 思思久热 | 国产精品一二区 | 日韩精品国产一区二区 | 老熟女国产| 成人伦理动 | 爱豆传媒免费播放 | 日韩综合在线视频 | 三级高清在线观看 | 日韩欧美国产超级视频 | 国产色护士 | 亚洲精品影视 | 日韩伦理剧在线观 | 成人羞羞在线观看网站 | 日韩在线视频在线 | 成人一区精品在线观看 | 夜福利网站 | 国产高潮白浆喷水男男 | 国产99在| 日韩精品国产一区二区 | A级毛片免费观看网站 | 91亚洲| 欧美成人精品欧美一 | 日韩av电影一区 | 日韩高清精品在线 | 一A级成人免费版 | 欧美国产精品 | 日韩精品激情在线播放 | 国产高清精品福 | 91一级在线| 久久中文娱乐网 | 日韩精品电影在线观看 | 午夜福利无码在线 | 日韩在线视频免费播放 | 国产精品码一本A片 | 欧美性爱视频网址 | 久久亚洲不卡 | 城中村嫖妓露脸自拍 | 黄色av网站免费观看 | 日韩亚洲欧美最大 | 玖草在线视频观看 | 欧美成人免费 | 国产又粗又黄视频 | 国产黑丝手机在线 | 国产夫妻精品 | 午夜爽爽影院 | 午夜啪视频 | 二区在线视频 | 日韩在线免费看网站 | 日韩欧美中文字幕一区 | 亚洲S色| 黃色A片三級三奶大 | 国产诱惑在线观看 | 欧美精品精品一区 | 高潮喷水在线 | 91视频地址 | 韩日在线视频观看 | 这里只有精品久久 | 国产三级黄色片 | 无码电影院 | 美国十次成人 | 91最新| 美女精品 | 国产97视频在线观看 | 无码免费在线不卡 | 国产午夜无码福利视频 | 偷拍自拍首页 | 真实国产亂伦免费, | 国产白丝袜 | 偷偷撸影院 | 国产三级片完整版 | 激情另类综合 | 国产xx00视频在 | 老湿机午夜福利 | 丁香综合网 | 欧美性爱日韩性爱 | 三级片成人在线 | 日韩视频小说在线观看 | 国产夫妻精品网 | 国产裸体美女免费 | 亚洲AV无码精品岛国 | 日韩欧美精品一区二区 | 97超碰大香蕉 | 国产清草 | 午夜视频日韩 | 在线精品秘| 国产情侣片 | 美女被干网站 | 日韩欧美色射高清 | 91精品久久久久久久 | 日韩女优在线观看 | 日韩资源站 |