<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    JUST DO IT ~

    我只想當(dāng)個程序員

    c# Anders Hejlsberg Bruce Eckel C#設(shè)計過程 Checked Exceptions 委托的概念

    (轉(zhuǎn))Anders Hejlsberg談C#設(shè)計過程
    博客分類: 程序開發(fā)
    C++CC#設(shè)計模式編程 
    原文:http://msdn.microsoft.com/vcsharp/headlines/hejlsberg/default.aspx 
        Anders Hejlsberg為Borland工作13個春秋后,于1996年加盟微軟,最初參與設(shè)計Visual J++和WFC(Windows Foundation Classes)。然后,擔(dān)任C#首席設(shè)計師和Microsoft .NET Framework設(shè)計小組核心成員。目前,他還將繼續(xù)領(lǐng)導(dǎo)C#語言后續(xù)版本的設(shè)計工作。 
      2003年7月30日,Hejlsberg在他微軟的辦公室會見了Bruce Eckel(《Thinking in C++》和《Thinking in Java》的作者)、Bill Venners(Artima.com主編)。談話內(nèi)容主要分為三個部分: 
      一、C#設(shè)計過程;語言可用性研究和語言美學(xué)。 
      二、Checked Exceptions特征的版本相關(guān)性和擴(kuò)展性。 
      三、委托的概念;組件概念在C#中的至高地位。 
      一 
      1、C#設(shè)計過程 
      Bruce Eckel:我聽說C#是一個工程師小組在一個屋子里設(shè)計出來的? 
      Anders Hejlsberg:是的。4年來,我們一直呆在這個屋子里。現(xiàn)在,每周一、三、五,我們?nèi)匀辉谶@里會面。 
      Bruce Eckel:我很想了解一些關(guān)于C#設(shè)計過程的情況。我直接或間接參與過幾種語言的設(shè)計工作,如Python。在Python設(shè)計過程中,Guido van Rossum被我們戲稱為“仁慈的獨(dú)裁者”。 
      Anders Hejlsberg:哦,Guido van Rossum就相當(dāng)于我的位置。 
      Bruce Eckel:那么你是C#小組“仁慈的獨(dú)裁者”么? 
      Anders Hejlsberg:我一般扮演最后拍板者的角色。比如,我們被一個問題困擾多時,到了非解決不可、只能作不二選擇的時候,是由我來作最后決定的。當(dāng)然大多數(shù)這樣的情況下,正確的選擇是顯而易見的。 
      Bruce Eckel:C#的設(shè)計過程是不是和Turbo Pascal、Delphi十分相似? 
      Anders Hejlsberg:后面兩者的設(shè)計過程不是那么規(guī)范的。因為Turbo Pascal主要由我一個人設(shè)計,而Delphi也是我和Chuck Jazdzewski、Gary Whizin等幾個為數(shù)不多的人來完成,所以沒有必要引入非常規(guī)范的設(shè)計過程。相反的,C#的設(shè)計過程則十分規(guī)范,每周一、三、五從1:00到3:00, 我們都會召開一個正式會議,會議議程也相當(dāng)靈活,所有的問題都會拿到桌面上公開討論、仔細(xì)推敲。我們還在互聯(lián)網(wǎng)上建立了一個Wiki,這些問題及其解決方案,以及其他一些相關(guān)的東西都被發(fā)布在上面。 
      Bruce Eckel:那你們是如何發(fā)現(xiàn)這些的問題呢? 
      Anders Hejlsberg:呵呵,我們有一套行之有效的方法。我們可以通過很多途徑來得到用戶對語言設(shè)計的反饋意見——如軟件設(shè)計咨詢會、網(wǎng)絡(luò)新聞組。這些反饋意見包括:疑問、軟件Bugs、不一致、不規(guī)范問題等。這樣我們就能有的放矢了。最后我們將這些問題整理成表,并一一重現(xiàn)它們。對于每個問題,我們都會認(rèn)真對待,詢問自己:“我們對這個問題有新的想法嗎?真的沒有嗎?這個問題已經(jīng)擱置好幾個星期了,我們立即花30分鐘集中精力研究一下,看這次是否能有所斬獲。” 
      Bruce Eckel:那可能一個問題長期沒有解決,都臭不可聞了…… 
      Anders Hejlsberg:也許有些臭問題只有放到下一個版本才能解決了。但是我認(rèn)為這樣一個過程可以保證不會遺漏任何問題,因為它們都被登記在冊。有時候,你面對這些問題呆坐了很長時間,可能也沒什么結(jié)果。但問題畢竟是被我們逮住了,總有一天會再去“拜訪”它的。也可能不會再去“拜訪”了,但問題終歸是不會被弄丟的。 
      Bill Venners:C#設(shè)計小組包含哪些成員,他們都擔(dān)當(dāng)什么角色? 
      Anders Hejlsberg:參與最初的C#設(shè)計的有Scott Wiltamuth、Peter Golde、Peter Sollich、Eric Gunnerson和我。C#2.0設(shè)計小組包括:Peter Hallam、Shon Katzenberger、Todd Proebsting,以及我自己。微軟研究院的Don Syme和Andrew Kennedy承擔(dān)了大部分一般性研究工作。 
      2、語言可用性研究和語言美學(xué) 
      Bill Venners:在C#的設(shè)計中,可用性研究、市場策略和語言美學(xué)的側(cè)重是如何權(quán)衡的? 
      Anders Hejlsberg:一般而言,好的語言設(shè)計過程體現(xiàn)了對設(shè)計小組成員的美學(xué)品味取向的綜合,也就是你剛才所說的語言美學(xué)觀。美學(xué)品味帶有極大的主觀性,很難定論,只有產(chǎn)品出來后,你才能仔細(xì)體味它。我認(rèn)為任何程度的可用性研究都不能取代語言美學(xué)的價值,因為可用性研究是極有針對性、非常具體的。可能有人問你:“你認(rèn)為這部分功能如何?”這個問題很難回答。“你對這個語言有什么看法?”你從何談起呢?你怎么可能花兩個小時就解決掉所有可用性問題?絕無可能。 
      Bruce Eckel:人們必須深入理解這個問題。 
      Anders Hejlsberg:使用一種編程語言會經(jīng)歷一個感覺微妙變化的過程。只有使用幾個月之后用戶才能真正喜歡上它。他們會逐漸發(fā)現(xiàn):“哦,它給人的感覺很舒服嘛。”你不能急于求成。 
      開始說過,我們在可用性研究上也做了大量工作,但主要是針對特定的功能。 
      Bill Venners:可以舉個例子么? 
      Anders Hejlsberg:我們將可用性研究的重點(diǎn)放在了IDE功能實現(xiàn)上。我們會問自己:“用戶是否知道在這里點(diǎn)擊右鍵會有什么結(jié)果?”在純語言功能部分,我們也考慮了一些可用性問題——例如在一些屬性和事件上——不過沒什么必要,真的。 
      我想在可用性研究上,語言特性不可能帶來和IDE特性一樣高的收益。你可以看到用戶點(diǎn)擊一個菜單項立即得到正確的反饋信息。而對語言來說,問題要復(fù)雜一些。例如:“它的概念容易理解么?”我們通過用戶咨詢會、留言板,比較好的解決了這些問題。用戶需要有個說話的地方,“對于這個新特性,我有這樣一些想法,你們是如何考慮的呢?”這樣的問題提得越多、尖銳越好,因為你肯定是最希望在產(chǎn)品出來以前就能知道用戶的想法,而不是產(chǎn)品推出以后。在一個語言特性被完全敲定前,我們通常都會考慮用戶的建議和意見的。 
      二 
      1、對Checked Exceptions特性持保留態(tài)度 
      (譯者注:在寫一段程序時,如果沒有用try-catch捕捉異常或者顯式的拋出異常,而希望程序自動拋出,一些語言的編譯器不會允許編譯通過,如Java就是這樣。這就是Checked Exceptions最基本的意思。該特性的目的是保證程序的安全性和健壯性。Zee&Snakey(MVP)對此有一段很形象的話,可以參見: 
      http://www.blogcn.com/user2/zee/main.asp。 
      Bruce Eckel 也有相關(guān)的一篇文章(《Does Java need Checked Exceptions》),參見: 
      http://www.mindview.net/Etc/Discussions/CheckedExceptions) 
      Bruce Eckel:C#沒有Checked Exceptions,你是怎么決定是否在C#中放置這種特性的么? 
      Anders Hejlsberg:我發(fā)現(xiàn)Checked Exceptions在兩個方面有比較大的問題:擴(kuò)展性和版本控制。我知道你也寫了一些關(guān)于Checked Exceptions的東西,并且傾向于我們對這個問題的看法。 
      Bruce Eckel:我一直認(rèn)為Checked Exceptions是非常重要的。 
      Anders Hejlsberg:是的,老實說,它看起來的確相當(dāng)重要,這個觀點(diǎn)并沒有錯。我也十分贊許Checked Exceptions特性的美妙。但它某些方面的實現(xiàn)會帶來一些問題。例如,從Java中Checked Exceptions的實現(xiàn)途徑來看,我認(rèn)為它在解決一系列既有問題的同時,付出了帶來一系列新問題的代價。這樣一來,我就搞不清楚Checked Exceptions特性是否可以真的讓我們的生活變得更美妙一些。對此你或許有不同看法。 
      Bruce Eckel:C#設(shè)計小組對Checked Exceptions特性是否有過大量的爭論? 
      Anders Hejlsberg:不,在這個問題上,我們有著廣泛的共識。C#目前在Checked Exceptions上是保持緘默的。一旦有公認(rèn)的更好的解決方案,我們會重新考慮,并在適當(dāng)?shù)牡胤讲捎玫摹N矣幸粋€人生信條,那就是——如果你對該問題不具有發(fā)言權(quán),也沒辦法推進(jìn)其解決進(jìn)程,那么最好保持沉默和中立,而不應(yīng)該擺出一個非此即彼的架勢。 
      假設(shè)你讓一個新手去編一個日歷控件,他們通常會這樣想:“哦,我會寫出世界上最好的日歷控件!我要讓它有各種各樣的日歷外觀。它有顯示部分,有這個,有那個……”他們將所有這些構(gòu)想都放到控件中去,然后花兩天時間寫了一個很蹩腳的日歷程序。他們想:“在程序的下一個版本中,我將實現(xiàn)更多更好的功能。” 
      但是,一旦他們開始考慮如何將腦海中那么多抽象的念頭具體實現(xiàn)出來時,就會發(fā)現(xiàn)他們原來的設(shè)計是完全錯誤的。現(xiàn)在,他們正蹲在一個角落里痛苦萬狀呢,他們發(fā)現(xiàn)必須將原來的設(shè)計全盤拋棄。這種情況我不是看到一次兩次了。我是一個最低綱領(lǐng)主義者。對于影響全局的問題,在沒有實際解決方案前,千萬不要將它帶入到整個框架中去,否則你將不知道這個框架在將來會變成什么樣子。 
      Bruce Eckel:極限編程(The Extreme Programmers)上說:“用最簡單的辦法來完成工作。” 
      Anders Hejlsberg:對呀,愛因斯坦也說過:“盡可能簡單行事。”對于Checked Excpetions特性,我最關(guān)心的是它可能給程序員帶來哪些問題。試想一下,當(dāng)程序員調(diào)用一些新編寫的有自己特定的異常拋出句法的API時,程序?qū)⒆兊枚嗝醇妬y和冗長。這時候你會明白Checked Exceptions不是在幫助程序員,反而是在添麻煩。正確的做法是,API的設(shè)計者告訴你如何去處理異常而不是讓你自己想破腦袋。 
      2、Checked Exceptions的版本相關(guān)性 
      Bill Venners:你提到過Checked Exceptions的擴(kuò)展性和版本相關(guān)性這兩個問題。現(xiàn)在能具體解釋一下它們的意思么? 
      Anders Hejlsberg:讓我首先談?wù)劙姹鞠嚓P(guān)性,這個問題更容易理解。假設(shè)我創(chuàng)建了一個方法foo,并聲明它可能拋出A、B、C三個異常。在新版的foo中,我要增加一些功能,由此可能需要拋出異常D。這將產(chǎn)生了一個極具破壞性的改變,因為原來調(diào)用此方法時幾乎不可能處理過D異常。 
      也就是說,在新版本中增加拋出的異常時,給用戶的代碼帶來了破壞。在接口中使用方法時也有類似的問題。一個實現(xiàn)特定功能的接口一經(jīng)發(fā)布,就是不可改變的,新功能只能在新版的接口中增加。換句話說,就是只能創(chuàng)建新的接口。在新版本中,你只有兩種選擇,要么建立一個新的方法foo2,foo2可以拋出更多的異常,要么在新的foo中捕獲異常D,并轉(zhuǎn)化為原來的異常A、B或者C。 
      Bill Venners:但即使在沒有Checked Exceptions特性的語言中,(增加新的異常)不是同樣會對程序造成破壞么?假如新版foo拋出了需要用戶處理的新的異常,難道僅僅因為用戶不希望這個異常發(fā)生,他寫代碼時就可以置之不理嗎? 
      Anders Hejlsberg:不,因為在很多情況下,用戶根本就不關(guān)心(異常)。他們不會處理任何異常。其實消息循環(huán)中存在一個最終的異常處理者,它會顯示一個對話框提示你程序運(yùn)行出錯。程序員在任何地方都可以使用try finally來保護(hù)自己的代碼,即使運(yùn)行時發(fā)生了異常,程序依然可以正確運(yùn)行。對于異常本身的處理,事實上,程序員是不關(guān)心的。 
      很多語言的throws語法(如Java),沒必要地強(qiáng)迫你去處理異常,也就是逼迫你搞清楚每一個異常的來源。它們要求你要么捕獲聲明的異常,要么將它們放入throws語句。程序員為了達(dá)到這個要求,做了很多荒謬可笑的事情。例如他們在聲明每個方法時,都必須加上修飾語:“throws Exception”。這完全是在搧這個特性的耳光,它不過是要求程序員多作些官樣文章,對誰都沒有好處。 
      Bill Venners:如此說來,你認(rèn)為不要求程序員明確的處理每個異常的做法,在現(xiàn)實中要適用得多了? 
      Anders Hejlsberg:人們?yōu)槭裁凑J(rèn)為(顯式的)異常處理非常重要呢?這太可笑了。它根本就不重要。在我印象中,一個寫得非常好的程序里,try finally和try catch語句數(shù)目大概是10:1。在C#中,也可以使用和類似try finally的using語句(來處理異常)。 
      Bill Venners:finally到底干了些什么? 
      Anders Hejlsberg:finally保證你不被異常干擾,但它不直接處理異常。異常處理應(yīng)該放在別的什么地方。實際上,在任何一個事件驅(qū)動的(如現(xiàn)代圖形界面)程序中,在主消息循環(huán)里,都有一個缺省的異常處理過程,程序員只需要處理那些沒被缺省處理的異常。但你必須確保任何異常情況下,原來分配的資源都能被銷毀。這樣一來,你的程序就是可持續(xù)運(yùn)行的。你肯定不希望寫程序時,在100個地方都要處理異常并彈出對話框吧。如果那樣的話,你作修改時就要倒大霉了。異常應(yīng)該集中處理,并在異常來臨處保護(hù)好你的代碼。 
      3、Checked Exceptions的擴(kuò)展性 
      Bill Venners:那么Checked Exceptions的擴(kuò)展性又是如何呢? 
      Anders Hejlsberg:擴(kuò)展性有時候和版本性是相關(guān)的。 在一個小程序里,Checked Exceptions顯得蠻迷人的。你可以捕捉FileNotFoundException異常并顯示出來,是不是很有趣?這在調(diào)用單個的API時也挺美妙的。但是在開發(fā)大系統(tǒng)時,災(zāi)難就降臨了。你計劃包含4、5個子系統(tǒng),每個子系統(tǒng)拋出4到10個異常。但是(實際開發(fā)時),你每在系統(tǒng)集成的梯子上爬一級,必須被處理的新異常都將呈指數(shù)增長。最后,可能每個子系統(tǒng)需要拋出40個異常。將兩個子系統(tǒng)集成時,你將必須寫80個throw語句。最后,可能你都無法控制了。 
      很多時候,Checked Exceptions都會激怒程序員,于是程序員就想辦法繞過這個特性。他要么在到處都是寫“throws Exception”,要么——我都不知道自己看到多少回了——寫“try, da da da da da(譯者注:意思是飛快的寫一段代碼), catch curly curly(譯者注:即‘{ }’)”,然后說:“哦,我會回頭來處理這些空的異常處理語句的。”實際上,理所當(dāng)然的沒有任何人會回頭干這些事情。這時候,Checked Exceptions已經(jīng)造成系統(tǒng)質(zhì)量的極大下降。 
      所以,你可能很重視這些問題,但是在我們決定是否將Checked Exceptions的一些機(jī)制放入C#時,卻是頗費(fèi)了一番思量的。當(dāng)然,知道什么異常可能在程序中拋出還是有相當(dāng)價值的,有一些工具也可以作這方面的檢查。我不認(rèn)為我們可以建立一套足夠嚴(yán)格而嚴(yán)謹(jǐn)?shù)囊?guī)則(來完成異常檢查),因為(異常)還可能是編譯器的錯誤引起的呢。但是我認(rèn)為可以在(程序)分析工具上下些功夫,檢測是否有可疑代碼,是否有未捕獲的異常,并將這些隱藏的漏洞給你指出來。 
      三 
      1、Simplicity和Simplexity 
      Bill Venners:C#和Java傳遞對象事件的方式有所不同。Java使用類(通常是內(nèi)部類(inner classes),它實現(xiàn)監(jiān)聽接口(listener interfaces)。C#使用了委托(delegates。譯者注:VJ++6.0就引入了delegates),它有點(diǎn)兒類似函數(shù)指針。為什么要采用委托方式呢? 
      Anders Hejlsberg:請允許我首先談?wù)剬τ谝话阋饬x上的Simplicity的看法。沒有任何人懷疑簡單的正確性,但是在如何實現(xiàn)簡單的問題上則千差萬別。有一種簡單,我想稱之為Simplexity。你做了一個很實際上復(fù)雜的東西,當(dāng)你將它包裝為一個簡單的東西時,通常是將它的復(fù)雜性隱藏起來。所以實際上,你并不是在設(shè)計一個真正簡單的系統(tǒng)。這樣的一個包裝過程,從某些角度上看,系統(tǒng)可能被你搞得更復(fù)雜了,因為用戶有時候需要知道被隱藏地東西。這就是我說的Simplexity。 
      對我而言,簡單必須是真正的簡單,也就是說,當(dāng)你將來某個時候需要鉆研系統(tǒng)內(nèi)部結(jié)構(gòu)時,它應(yīng)該顯得更加簡單,而不是比它表面那個樣子更復(fù)雜。 
      2、委托和接口 
      Anders Hejlsberg:委托提供了與類和接口無關(guān)的實現(xiàn)方式,這是我認(rèn)為最重要的地方。過去的很多編程語言已經(jīng)認(rèn)識到了這種方式的重要性。這種方式有很多名字,如:函數(shù)指針、成員函數(shù)指針,在Lisp語言中,被稱為closures, 它是非常有用處的。 
      Bill Venners:那么這是如何實現(xiàn)的呢? 
      Anders Hejlsberg:使用接口的確可以完成委托具有的所有功能,但是你會被迫面對煩雜的“家務(wù)管理”。我們可以對比一下Java和.NET處理事件的過程。因為在Java中沒有委托,所以最終必須使用接口。 
      接口是對應(yīng)于事件的,一個接口可以定義1、2、3、4個甚至更多的方法。這點(diǎn)就已經(jīng)產(chǎn)生了問題,因為這個“對應(yīng)”沒有明確的規(guī)范。到底應(yīng)該定義多少個接口來處理事件呢?是每個事件對應(yīng)一個接口還是所有的事件用一個接口?讓你難以取舍。好了,先胡亂選擇其一去處理組件的事件。接下來,你必須實現(xiàn)這些接口。理所當(dāng)然的,如果處理兩個組件的同樣的事件,你就必須將接口實現(xiàn)兩次——你當(dāng)然不愿意這么干,所以在這種情況下,你還需要創(chuàng)建一個適配器。這樣,煩雜的“家務(wù)管理”就跑到你面前來了。 
      內(nèi)部類在處理家務(wù)事上能幫點(diǎn)小忙,但最終,有些問題你是逃避不了的——事件接收者必須知道它什么時候接收事件。這樣你又必須明確的實現(xiàn)一個監(jiān)聽接口。與此相反,如果你使用委托,只要信息是兼容的,你就可以將所有事件放在一起處理。這家伙并不關(guān)心自己是怎么被調(diào)用的,它僅僅是一個(處理事件的)方法。 
      Bruce Eckel:看來,委托是非常精瘦的。 
      Anders Hejlsberg:對,的確如此。 
      Bruce Eckel:它也更加靈活。 
      Anders Hejlsberg:的確是這樣。它僅僅依賴于信息的兼容性,如參數(shù)是否一致。如果是兼容的,你就可以將多個事件放在一起處理。從概念上說,這也完全滿足了用戶對一個回調(diào)的結(jié)果期望,對吧?只要給我一些參數(shù),我就可以編寫程序了。聽起來很像一個方法吧,那么我給定該方法的一個引用,這個引用就是我所說的委托了。 
      Bruce Eckel:最后你也不會丟掉類型檢查。類型檢查是在運(yùn)行時進(jìn)行的么? 
      Anders Hejlsberg:不,大多數(shù)都是在編譯時進(jìn)行。你創(chuàng)建一個委托的實例后,它就和程序員在C++中使用的成員函數(shù)指針差不多。委托指向了某對象的一個方法。如果是虛擬的方法,你也能準(zhǔn)確地判斷委托的指向。所以從某種意義上說說,你可以在實例化委托時就解決虛擬問題。通過委托實現(xiàn)的調(diào)用可以看作一個間接的(方法)調(diào)用指令。 
      Bruce Eckel:除此之外,就不再需要其他的間接支持了。 
      Anders Hejlsberg:是的,構(gòu)造一個委托的時候,你就可以一次性解決虛擬函數(shù)表(VTBL)和委托的指向問題;通過委托實現(xiàn)的調(diào)用都可以直接準(zhǔn)確的得到它對應(yīng)的方法。所以,委托比接口派遣更有效率,即使和標(biāo)準(zhǔn)的方法派遣相比,它的效率也要高一些。 
      Bruce Eckel:C#中也有Multicast類型的委托(譯者注:Multicast即多點(diǎn)傳送。是指一個委托可以對應(yīng)多個方法;委托被調(diào)用時,就可以引起多個方法的調(diào)用。更詳細(xì)的說明可以參考:http://msdn.microsoft.com/vjsharp/productinfo/ 
      visualj/visualj6/technical/articles/general/delegates/), 它能夠使多個函數(shù)被調(diào)用。 這是一個orthogonal特性嗎? 
      Anders Hejlsberg:Multicast是一個徹頭徹尾的orthogonal特性。老實說,在Multicast是否重要這個問題,我也是持保留態(tài)度的。我承認(rèn)它有它的用處,但保守地講的話,我認(rèn)為所有的委托都是single cast的。有些人覺得Multicast十分重要,使用它有很多優(yōu)點(diǎn),但是在大多數(shù)情況下委托恰恰都是single cast的。事實上,我們構(gòu)造的(C#)系統(tǒng)是使用single cast的,只要你(在這個系統(tǒng)里)不使用Multicast,就不會為它付出什么代價的。 
      Bill Venners:委托是怎么來體現(xiàn)你前面所說的 Simplicity和 Simplexity的呢?它們都體現(xiàn)在哪些地方? 
      Anders Hejlsberg:如果你仿效接口來實現(xiàn)委托,那么你最終無可避免地要面對“家務(wù)管理”和適配器問題。其實,我們可以觀察任何一個捆綁了JavaBeans的開發(fā)工具,它們都會生成一些適配器并告訴你:“你不要修改下面的代碼,我會分發(fā)一些非常瘦小的幫助類給你。”這對于我來說就太復(fù)雜了。它并不是一個真正簡單的系統(tǒng),它實際上非常復(fù)雜,不過是貌似簡單而已。 
      3、組件概念在C#中的至高地位 
      Bill Venners:O'Reilly網(wǎng)站發(fā)布過對于你的一次采訪,當(dāng)時你這么評價C#對于屬性和事件的支持:“現(xiàn)在,程序員人員每天都在開發(fā)大量的軟件組件。他們不是在開發(fā)彼此孤立的應(yīng)用程序和類庫。每個人都在開發(fā)繼承自環(huán)境提供的組件的新組件。這些新組件覆蓋了父組件的一些方法、屬性,處理一些事件,然后將這些新組件放回去(到組件庫)。這是一個首先要樹立的概念。” 
      我希望能更好理解你的談話精神,因為我一直認(rèn)為我是在開發(fā)類而不是組件。你是說很多人都在為別人開發(fā)在Delphi、VB和Java中使用的組件么?你說的“組件”到底是什么呢? 
      Anders Hejlsberg:“組件”一詞包含的最重要的意思是組件可以很好的移植。這聽起來十分美妙,但我們可能對此有不同的理解。在一個最簡單的form中,一個組件可能就等同于一個附加了一些數(shù)據(jù)的類。組件是一個獨(dú)立的軟件部件,并不僅僅包含代碼和數(shù)據(jù)。它是一個通過屬性、方法和事件來實現(xiàn)自我暴露的類;是一個包含了元數(shù)據(jù)、命名模式等很多附加特征的類。這些特征可以給特定的開發(fā)環(huán)境提供動態(tài)信息,如:組件怎么使用,組件如何持久化自己的數(shù)據(jù)。開發(fā)環(huán)境使用組件的元數(shù)據(jù)就能夠?qū)崿F(xiàn)組件功能的智能解釋并給出相應(yīng)的說明文檔。“組件”包含了如上所述的全部(內(nèi)容)。 
      Bill Venners:我使用Java作開發(fā)時想到的是,我是在開發(fā)類庫而不是組件庫,可能是因為我覺得get/set太笨重了。在激發(fā)事件的時候,我也使用get/set,但我沒有打算將這些類拿到集成開發(fā)環(huán)境中去使用,我一直想象這些類就是給那些那些純編碼的人們使用的。所以我很想知道現(xiàn)在到底有多少人在開發(fā)類似JavaBean那樣的組件,面向組件開發(fā)是否是未來的趨勢,因為在我的職業(yè)生涯中,和組件打的交道太少了。 
      Anders Hejlsberg:當(dāng)今,主流的面向?qū)ο缶幊陶Z言實際上都是混血兒。其中有大量的結(jié)構(gòu)化編程,對象基本上也不過是一個包含了一系列方法和一個this指針的結(jié)構(gòu)體。當(dāng)你想到一個對象或者組件時,我想,從概念來說,你應(yīng)該意識到它是有屬性和事件的。如果編程語言能給予這些概念頭等待遇的話,理解它就要容易一些。 
      有人可能說,C#對于屬性和事件的支持不過就是個“甜果”(譯者注:原文為syntactic sugar。Peter Landin發(fā)明的一個術(shù)語,意思是“增加到語言中、讓人們感覺更舒服的特性”)而已。其實它的確就是個甜果,對不對?哼,對象本來就是個甜果嘛。我們不過就是在那些虛擬函數(shù)表(VTBL)上打轉(zhuǎn),你用C語言的宏也能實現(xiàn),對吧?真的,你可以用C語言來面向?qū)ο缶幊蹋贿^它的紛繁復(fù)雜能讓你墜入地獄。同樣的,你可以在C++或者Java里編寫組件,但是因為這些語言沒有給予組件概念足夠重要的地位,所以要痛苦得多。還得說明一下的是,屬性,并不是真的是一個屬性,而是getBla和setBla(兩個方法)。在屬性觀察器中,你看到的是Bla,但你應(yīng)該知道它內(nèi)部映射到了get/set。 
      很明顯,組件導(dǎo)向是大勢所趨。我們就是通過組件來使用我們的類的,但是在大多數(shù)語言中,組件并沒有成為最重要的概念。我想強(qiáng)調(diào)的是,組件應(yīng)該擁有頭等地位。對于PME編程模式——屬性、方法、事件——的談?wù)撘呀?jīng)持續(xù)了很長的時間,我們也都在日復(fù)一日的使用這些東西來編程,那為什么不在編程語言中給予它應(yīng)有的至高待遇呢? 
      附注:Anders Hejlsberg簡歷 
      Anders Hejlsberg是Microsoft公司卓越的軟件工程師,領(lǐng)導(dǎo)設(shè)計了C#(發(fā)音為“C Sharp”)編程語言。Hejlsberg于上個世紀(jì)80年代初投身軟件事業(yè),為MS-DOS和CP/M平臺開發(fā)了Pascal編譯器。成立不久的一家小公司——Borland——很快聘用了Hejlsberg并收購了他的編譯器,然后改名為Turbo Pascal。Hejlsberg接下來領(lǐng)導(dǎo)開發(fā)了Turbo Pascal的替代產(chǎn)品:Delphi。1996年,Hejlsberg在為Borland工作13個春秋后,加盟Microsoft公司(譯者注:因為Borland公司內(nèi)部矛盾和Microsoft的殷勤)。


    轉(zhuǎn)自http://www.cnitblog.com/sugar/archive/2006/03/19/7773.html 

    [人物介紹] 
        Anders Hejlsberg,微軟著名工程師,帶領(lǐng)他的小組設(shè)計了C#(讀作:C-Sharp)程序設(shè)計語言。Hejlsberg第一次登上軟件界歷史舞臺是在80年代早期,因為他為MS-DOS和CP/M設(shè)計了Pascal編譯器。當(dāng)時,還是一個小公司的Borland很快雇用了他,并買下了他的編譯器,改稱Turbo Pascal。在Borland,Hejlsberg繼續(xù)開發(fā)Turbo Pascal,并最終帶領(lǐng)他的小組設(shè)計了Turbo Pascal的替代品:Delphi。1996年,在進(jìn)入Borland 13年后,Hejlsberg加入了微軟。最初,他做Visual J++和Windows Fundatioin Classes(WFC)的架構(gòu)師。隨后,Hejlsberg成為C#的首席設(shè)計師和.NET Framework的關(guān)鍵參與者。目前,Anders Hejlsberg還在領(lǐng)導(dǎo)著C#程序設(shè)計語言的繼續(xù)開發(fā)。 

        Bruce Eckel,Think in C++(C++編程思想)和Think in Java(Java編程思想)的作者。 

        Bill Venners,Artima.com的主編。 

    [內(nèi)容] 
        一、泛型概述 
        二、C#中的泛型 
        三、C#泛型和java泛型的比較 
        四、C#泛型和C++模板的比較 
        五、C#泛型中的約束 



    一、泛型概述 

        Bruce Eckel:您能對泛型做一個快速的介紹么? 
        Anders Hejlsberg:泛型其實就是能夠向你的類型中加入類型參數(shù)的一種能力,也稱作參數(shù)化的類型或參數(shù)多態(tài)性。最著名的例子就是List集合類。一個List是一個易于增長的數(shù)組。它有一個排序方法,你可以為它做索引,等等。現(xiàn)在,如果沒有參數(shù)化的類型,那么不論使用數(shù)組還是使用List都不是很好。如果你使用數(shù)組,你能獲得強(qiáng)類型,因為你可以聲明一個 Customer類型的數(shù)組,但你失去了可增長性和那些方便的方法;如果你使用一個List,你能夠得到所有的便利,但你失去了強(qiáng)類型。你難以說出一個 List是什么(類型的)List,它只是一個Object的List【譯注:“什么類型的List”指的是List存放的元素是什么類型的】。這會給你帶來麻煩,因為類型只能在運(yùn)行形時進(jìn)行檢查,也就是說在編譯時不會進(jìn)行類型檢查。就算你硬要把一個Customer放進(jìn)一個List并試圖從中得到一個 String,編譯器也不會不高興。在運(yùn)行之前你根本無法發(fā)現(xiàn)它不能工作。同時,當(dāng)你將簡單類型【譯注:指值類型】放入List時,還必須對它們進(jìn)行裝箱。正是由于這些問題,你不得不在List和數(shù)組之間徘徊,你經(jīng)常要很痛苦地決定應(yīng)該使用哪一個。 
        泛型的偉大之處在于你現(xiàn)在可以盡情地享受你的蛋糕了,因為你能夠定義一個List<T>(讀作:List of T)【譯注:中文可以說成“T類型的List”】。當(dāng)你使用List時,你居然能夠說出它是什么類型的List,并且你將獲得強(qiáng)類型,編譯器會為你檢查它的類型。這些只是直覺上的好處,它還有其它許多優(yōu)點(diǎn)。當(dāng)然,你并不是只能將它用于List,Hastable、Dictionary(將鍵影射到值上的數(shù)據(jù)結(jié)構(gòu))——所有你想調(diào)用的都行。你可能想將String影射到Customer、將int影射到Order,在這些情況下你都能獲得強(qiáng)類型。 

    二、C#中的泛型 

        Bill Venners:泛型在C#中是如何工作的呢? 
        Anders Hejlsberg:在沒有泛型的C#中,你只能寫class List {...};而在帶有泛型的C#中,你可以寫class List<T> {...},這里的T是一個類型參數(shù)。在List<T>中,你可以把T就當(dāng)作一個類型來用。當(dāng)它實際用來建立一個List對象時,你要寫 List<int>或List<Customer>。這樣你就從List<T>構(gòu)造了一個新的類型,看起來就好像你用你的類型變量替換了所有的類型參數(shù)。所有的T都變成了int或Customer,你無須進(jìn)行向下轉(zhuǎn)換,它們是強(qiáng)類型的,任何時候都會被檢查。 
        在 CLR(Common Language Runtime,公共語言運(yùn)行時)中,當(dāng)你編譯List<T>或其它泛型類型時,它們和普通類型一樣被轉(zhuǎn)換為IL(Intermediate Language,中間語言)和元數(shù)據(jù)。IL和元數(shù)據(jù)帶有附加信息,可以知道這是一個類型參數(shù),當(dāng)然,原則上泛型類型的編譯和其它類型一樣。在運(yùn)行時,當(dāng)你的應(yīng)用程序第一次引用List<T>時,系統(tǒng)會看看你是否已經(jīng)使用過List<int>。如果沒有,它會調(diào)用JIT將帶有 int類型變量的List<T>編譯為IL和元數(shù)據(jù)。當(dāng)JIT即時編譯IL時,同樣會替換類型參數(shù)。 

        Bruce Eckel:所以它是在運(yùn)行時被實例化的。 
        Anders Hejlsberg:它確實是在運(yùn)行時實例化。它在需要的時候才產(chǎn)生特定的原生代碼(native code)。字面上,當(dāng)你說List<T>時,你會得到一個int類型的List。如果泛型類型中使用的是T類型的數(shù)組,它會變成int類型的數(shù)組。 

        Bruce Eckel:這個類會在某一時刻被垃圾收集器收集么? 
        Anders Hejlsberg:是也不是,這是一個正交的問題。它會在該程序集中建立一個類,這個類在程序集中會一直存在。如果你終止了程序集,這個類會消失,和其它類一樣。 

        Bruce Eckel:但如果我的程序中聲明了一個List<int>和一個List<Cat>,但我從未使用過List<Cat>…… 
        Anders Hejlsberg:…… 那么系統(tǒng)不會實例化List<Cat>。當(dāng)然,下面的情況除外。如果你使用NGEN產(chǎn)生一個鏡像,也就是說如果你預(yù)先生成了一個原生代碼的鏡像,會預(yù)先實例化。但是如果你在一般的環(huán)境下運(yùn)行,則這個實例化是純需求驅(qū)動(demand driven)的,會盡可能地延遲【譯注:正如上面所說,直到使用時才進(jìn)行實例化】。 
        實際上,我們所要進(jìn)行實例化的所有類型都是值類型——如List<int>、List<long>、List<double>、 List<float>——我們?yōu)槊恳粋€都建立一份唯一的可執(zhí)行原生代碼的拷貝。因此,List<int>有它自己的代碼,List<long>有它自己的代碼,List<float>有它自己的代碼。對于所有的引用類型我們共享它們的代碼,因為它們在表現(xiàn)上是一樣的,它們只是一些指針。 

        Bruce Eckel:因此你只需要轉(zhuǎn)換。 
        Anders Hejlsberg:不,實際上是不需要的。我們可以共享原生鏡像,但他們實際上具有獨(dú)立的VTable。我要指出的是,我們只是盡量對代碼進(jìn)行有意義的共享,但我們很清楚,為了效率,有很多代碼是不能共享的。典型的就是值類型,你會很關(guān)心List<int>中到底是不是int。你肯定不希望將它們被裝箱為 Object。對值類型進(jìn)行裝箱是一種共享的方法,但對它們進(jìn)行裝箱開銷會很大。 

        Bill Venners:對于引用類型,所不同的只是類。List<Elephant>不同于List<Orangutan>,但他們實際上共享了所有方法的代碼。 

        Anders Hejlsberg:是的。作為實現(xiàn)的細(xì)節(jié),它們實際上共享了相同的原生代碼。 

    三、C#泛型和java泛型的比較 

        Bruce Eckel:如何比較C#中的泛型和java中的泛型呢? 
        Adners hejlsberg:Java 的泛型最初是基于Martin Odersky和其它人一起做的稱作Pizza的一個項目的。Pizza后改名為GJ,然后成為JSR,最后以被Java語言收容而告終。這種泛型以能夠在原有的VM(Virtual Machine,虛擬機(jī))上運(yùn)行為關(guān)鍵設(shè)計目標(biāo)。也就是說,你不必修改你的VM,但它會帶來很多限制。這些限制并不會很快出現(xiàn),但很快你就會說:“嗯,這有點(diǎn)陌生。” 
        例如,使用Java泛型,我覺得你實際上不會獲得任何的執(zhí)行效率,因為當(dāng)你編譯一個Java泛型類時,編譯器會將所有的類型參數(shù)替換為Object。當(dāng)然,如果你嘗試建立一個List<int>,你就需要對所有的int進(jìn)行裝箱。因此,這會有很大的開銷。另外,為了讓VM高興,編譯器必須為所有的類型插入類型轉(zhuǎn)換。如果一個List是Object的,而你想將這些Object視為 Customer,就必須將Object轉(zhuǎn)換為Customer,以讓類型檢查器滿意。而它在實現(xiàn)這些的時候,真的只是為你插入所有這些類型轉(zhuǎn)換。因此,你只是嘗到了語法上的甜頭,卻沒有獲得任何執(zhí)行效率。所以我覺得這是(泛型的)Java實現(xiàn)的頭號問題。 
        第二號問題,我覺得也是一個很嚴(yán)重的問題,這就是由于Java泛型是依靠消除所有的類型參數(shù)來實現(xiàn)的,你就無法在運(yùn)行時獲得一個和編譯時同樣可靠的表現(xiàn)。當(dāng)你在 Java中反射一個泛型的List的時候,你無法得知這是個List什么類型的List。它只是一個List。因為你失去了類型信息,任何由代碼生成方案或基于反射的方案所產(chǎn)生的動態(tài)類型都將無法工作。唯一讓我認(rèn)為清晰的趨勢就是,越來越多的東西將不能運(yùn)行,就是因為你丟掉了類型信息。但在我們的實現(xiàn)中,所有這些信息都是可用的。你可以使用反射來獲得List<T>對象的System.Type。但你還不能建立它的一個實例,因為你并不知道T 是什么。但是接下來你可以使用反射來獲得int的Sytem.Type。然后你就可以請求反射將這兩個System.Type結(jié)合起來并建立一個 List<int>,然后你還能獲得List<int>的另一個System.Type。因此,所有你在編譯期間能做的在運(yùn)行時同樣可以。 

    四、C#泛型和C++模板的比較 

        Bruce Eckel:如何比較C#泛型和C++模板呢? 
        Anders Hejlsberg:我認(rèn)為對C#泛型和C++模板之間的區(qū)別最好的理解是:C#泛型更像類,只不過它帶有類型參數(shù);C++模板接近宏,只不過它看起來像類。 
        C# 泛型和C++模板之間最大的區(qū)別在于類型檢查發(fā)生的時機(jī)和如何進(jìn)行實例化。首先,C#在運(yùn)行時進(jìn)行實例化。而C++在編譯時,或者可能是連接時進(jìn)行實例化。不管怎么說,C++是在程序運(yùn)行前進(jìn)行實例化。這是第一點(diǎn)不同。第二點(diǎn)不同是當(dāng)你編譯泛型類型時,C#會進(jìn)行強(qiáng)類型檢查。對于一個非約束的類型參數(shù),如List<T>,能夠在類型為T的值上執(zhí)行的方法僅僅是那些能夠在Object類型中找到的方法,因為只有這些方法是我們能夠保證存在的。在C#中,我們要保證在一個類型參數(shù)上執(zhí)行的所有操作都能成功。 
        C++正相反。在C++中,你可以在類型參數(shù)所指定的類型的變量上執(zhí)行你想做的任何操作。但是一旦你對它進(jìn)行了實例化,它就有可能無法工作,你將會得到一些含義模糊的錯誤信息。例如,如果你有一個類型參數(shù) T,而x和y是T類型的變量,然后你執(zhí)行x+y,如果你對兩個T定義了一個operator+還好說,否則你就只能得到一些沒意義的錯誤消息。因此,從某種意義上說,C++模板實際上是無類型的,或者說是弱類型的。而C#泛型是強(qiáng)類型的。 

    五、C#泛型中的約束 

        Bruce Eckel:約束是如何在C#泛型中工作的呢? 
        Anders Hejlsberg:在C#泛型中,我們能夠為類型參數(shù)施加約束。以我們的List<T>為例,你可以說class List<T> where T : IComparable。這意味著T必須實現(xiàn)IComparable接口。 

        Bruce Eckel:有意思。在C++中,約束是隱式的。 
        Anders Hejlsberg:是的。在C#中我們也可以這樣做。譬如我們有一個Dictionary<K, V>,它有一個Add()方法,這個方法帶有K key和V value參數(shù)。Add()方法的實現(xiàn)將希望能夠?qū)鬟f進(jìn)來的key和Dictionary中已經(jīng)存在的key進(jìn)行比較,而且它希望使用一個稱作 IComparable的接口。唯一的途徑就是將key參數(shù)轉(zhuǎn)換為IComparable接口,然后調(diào)用CompareTo方法。當(dāng)然,當(dāng)你這么做的時候,你就為K類型和key參數(shù)建立了一個隱式的約束。如果傳遞進(jìn)來的key沒有實現(xiàn)IComparable接口,你會得到一個運(yùn)行時錯誤。這在你的所有方法中都有可能出現(xiàn),因為你的約定沒有要求key必須實現(xiàn)IComparable接口。當(dāng)然,你還得為運(yùn)行時類型檢查付出代價,因為你實際上進(jìn)行了動態(tài)類型轉(zhuǎn)換。 
        使用約束,你可以消除代碼中的動態(tài)檢查,而在編譯時或裝載時進(jìn)行。當(dāng)你要求K必須實現(xiàn)IComparable接口時,會發(fā)生很多事情。對于K類型的值,你現(xiàn)在可以直接訪問接口方法而無需類型轉(zhuǎn)換。因為程序在語義上可以保證它實現(xiàn)了這個接口。無論什么時候你嘗試建立這個類型的一個實例時,編譯器都會檢查這些類型是否實現(xiàn)了這個接口,如果沒有實現(xiàn),會給你一個編譯錯誤。如果你使用的是反射,你會得到一個異常。 

        Bruce Eckel:你是說編譯器和運(yùn)行時(都會進(jìn)行檢查)? 
        Anders Hejlsberg:編譯器會檢查它,但你仍有可能在運(yùn)行時通過反射來做這些,因此系統(tǒng)還會檢查它。正像我前面說的,編譯時可以做的任何事都可以在運(yùn)行是通過反射來做。 

        Bruce Eckel:我可以做一個函數(shù)模板,換句話說,一個帶有不知道類型的參數(shù)的函數(shù)?你為約束添加了強(qiáng)類型檢查,但我是不是能像C++模板那樣得到一個弱類型模板?例如,我能否寫一個函數(shù),它帶有兩個參數(shù)A a和B b,并在代碼中寫a+b?我能不能說我不在乎對于A和B是否有operator+,因為它們是弱類型的? 
        Anders Hejlsberg:你真正要問的問題應(yīng)該是這在約束中如何說吧?約束,和其他特性一樣,最終將可以是任意復(fù)雜的。當(dāng)你考慮它的時候,約束只是一個模式匹配機(jī)制。你可能希望能夠說“這個類型參數(shù)必須有一個帶有兩個參數(shù)的構(gòu)造器、實現(xiàn)了operator+、有這個靜態(tài)方法、有那兩個實例方法、等等”。問題是,你希望這種模式匹配機(jī)制有多復(fù)雜? 
        從沒有任何東西到完全模式匹配是一個整個的連續(xù)體。沒有任何東西(的模式匹配)太小了,不能說明問題;而完全模式匹配又太復(fù)雜了,因此我們需要在中間找一個平衡點(diǎn)。我們允許你將約束指定為一個類、一個或多個接口,以及一些構(gòu)造器約束。譬如,你可以說:“這個類型必須實現(xiàn)IFoo和IBar”或“這個類型必須繼承基類X”。一旦你這么做了,在編譯時和運(yùn)行時都會檢查這個約束是否為真。這個約束所隱含的任何方法對于類型參數(shù)所指定的類型的值都是直接有效的。 
        現(xiàn)在,在C#中,運(yùn)算符是靜態(tài)成員。因此,運(yùn)算符不能是接口的成員,因此接口約束不能帶給你operator+。你只能通過類約束獲得operator+,你可以說這個類型參數(shù)必須繼承自比如說Number類,并且 Number類對于兩個Nubmer有operator+。但你不能抽象地說“必須有一個operator+”,我們無法知道這句話的具體含義。 

        Bill Venners:你通過類型進(jìn)行約束,而不是簽名。 
        Anders Hejlsberg:是的。 

        Bill Venners:因此這個類型必須擴(kuò)展一個類或?qū)崿F(xiàn)一個接口。 
        Anders Hejlsberg:是的。而且我們還能夠走得更遠(yuǎn)。實際上我們也想過再走遠(yuǎn)一些,但這會變得相當(dāng)復(fù)雜。而且增加的復(fù)雜性與所得到的相比很不值得。如果你想做的事情在約束系統(tǒng)中不直接支持,你可以使用一個工廠模式。例如你有一個Martix<T>,而在這個Martix(矩陣)中,你可能想定義一個“點(diǎn)乘”【譯注:矩陣上的一種乘法運(yùn)算,另一種稱為“叉乘”】方法。這意味著你最終將要考慮如何將兩個T相乘,但你不能將這說成是一個約束,至少當(dāng)T不是int、 double或float時你不能這么說。但你可以讓你的Martix帶有一個Calculator<T>作為參數(shù),而在 Calculator<T>中,有一個稱為Multiply的方法。你可以在其中進(jìn)行實現(xiàn),并將結(jié)果傳遞給Martix。 

        Bruce Eckel:而且Calculator也是一個參數(shù)化的類型。 
        Anders Hejlsberg:是的。這有些像工廠模式,還有很多方法可以做到,這也許不是你最喜歡的方法,但做任何事情都要付出代價。 

        Bruce Eckel: 是呀,我開始認(rèn)為C++模板是一種弱類型機(jī)制。而當(dāng)你想其中添加了約束后,你從弱類型走向了強(qiáng)類型。但這一定會帶來更多的復(fù)雜性。這就是代價吧。 
        Anders Hejlsberg: 關(guān)于類型你可以認(rèn)為它是一個標(biāo)尺。這個標(biāo)尺定得越高,程序員的日子就會越不好過,但更高的安全性隨之而來。但你可以把這個標(biāo)尺向任何一個方向調(diào)節(jié)。

    posted on 2015-04-12 11:38 小高 閱讀(231) 評論(0)  編輯  收藏 所屬分類: java基礎(chǔ)DotNetException 異常處理

    導(dǎo)航

    <2015年4月>
    2930311234
    567891011
    12131415161718
    19202122232425
    262728293012
    3456789

    統(tǒng)計

    常用鏈接

    留言簿(3)

    隨筆分類(352)

    收藏夾(19)

    關(guān)注的blog

    手冊

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 国产色在线|亚洲| 国产亚洲午夜精品| 国产成人免费爽爽爽视频| 亚洲一级毛片在线播放| 日韩毛片免费无码无毒视频观看| 亚洲人成人伊人成综合网无码 | 国产gv天堂亚洲国产gv刚刚碰| 一区二区三区免费在线观看| 国产亚洲精品成人AA片新蒲金| a毛片免费全部播放完整成| 久久久久久亚洲精品成人| 波多野结衣在线免费视频| 亚洲国产成人AV在线播放| 亚洲免费在线观看| 国产精品99精品久久免费| 亚洲国产成AV人天堂无码| 国产精品视_精品国产免费| 男男gay做爽爽的视频免费| 亚洲成av人片天堂网老年人| 色www永久免费网站| 亚洲白色白色永久观看| 四虎影院在线免费播放| 中文字幕免费在线看电影大全| 亚洲影院在线观看| 四虎免费在线观看| 成av免费大片黄在线观看| 亚洲美女大bbbbbbbbb| 四虎永久免费影院| 无码国产精品一区二区免费模式| 亚洲大香人伊一本线| 免费大黄网站在线观| 日韩精品无码一区二区三区免费| 亚洲精品宾馆在线精品酒店| 亚洲一区爱区精品无码| 麻豆最新国产剧情AV原创免费| 人禽伦免费交视频播放| 亚洲男女性高爱潮网站| 一本色道久久88亚洲综合| 国产成人免费午夜在线观看| 又长又大又粗又硬3p免费视频| 亚洲天堂电影在线观看|