一次公司内部的Tech Talk中涉及到的关于语言的发展问题
C#從一開始就是強(qiáng)類型語(yǔ)言,直到現(xiàn)在都堅(jiān)持早綁定。對(duì)于晚綁定,C#只是一種選擇,可能以不同于VB的方式來(lái)做而且會(huì)有所改進(jìn)。對(duì)于老式的弱類型對(duì)象模型來(lái)說(shuō),比如OLE,如果從晚綁定角度出發(fā),會(huì)比從早綁定好討論,因?yàn)檫@種對(duì)象模型無(wú)非就是對(duì)象若干方法的交互。
?????? 當(dāng)你查看一個(gè)大函數(shù)內(nèi)部嵌套很深的結(jié)構(gòu),比如FOR循環(huán)時(shí),語(yǔ)言是何時(shí),如何處理變量捕獲、如何進(jìn)行實(shí)例保護(hù)就非常不同。在C#中,每次循環(huán)時(shí)實(shí)例都被保護(hù),而VB有點(diǎn)像JavaScript,變量被隱性提升到函數(shù)頂部。所以,在變量捕獲方面也存在語(yǔ)義上的區(qū)別。有時(shí)這些區(qū)別及其細(xì)微,必須用非常變態(tài)的程序才能觀察到。
?????? LINQ, 函數(shù)型語(yǔ)言風(fēng)格,所有的東西都基于表達(dá)式。“表達(dá)式”從其定義來(lái)說(shuō)就是可組合的。不斷提高程序員的生產(chǎn)率:提升抽象的層次——垃圾回收機(jī)制,類型安全,異 常處理,全新的“聲明型”編程語(yǔ)言等。聲明型語(yǔ)言獲得了更高層次的“可組合性”。當(dāng)我們更多的使用“函數(shù)型”或者“聲明型”風(fēng)格的編程時(shí),更有可能把運(yùn)行 時(shí)框架構(gòu)建得能更好的發(fā)揮多核的優(yōu)勢(shì),更好的處理并發(fā)。如果以“命令型”風(fēng)格來(lái)工作,能發(fā)揮的余地就很小,因?yàn)闊o(wú)法預(yù)見所有動(dòng)作,必須串行執(zhí)行否則不可預(yù) 料。
?????? CLR以 及目前絕大多數(shù)的運(yùn)行時(shí)都是“命令型”引擎,指令都相當(dāng)傳統(tǒng),比如堆棧增長(zhǎng);它們擁有易變的狀態(tài),包括易變的全局狀態(tài)等等。在此之上,之所以能進(jìn)行“函數(shù) 型”編程,是因?yàn)椤昂瘮?shù)型”編程本質(zhì)上是“命令型”編程所具備的能力集的一個(gè)子集。最大化這種靈活性,使“函數(shù)型”能力子集越來(lái)越相關(guān)和主流化。
???? “函數(shù)型”語(yǔ)言是“串行執(zhí)行”的好藥方。要想使“函數(shù)型”語(yǔ)言運(yùn)轉(zhuǎn)良好,關(guān)鍵點(diǎn)并不是處理好基本的表達(dá)式問(wèn)題,而是處理好lambda表達(dá)式和副作用的問(wèn)題,使能夠?qū)⒈磉_(dá)式作為第一級(jí)的編程要素來(lái)使用,能夠指出lambda表達(dá)式和Closure(函數(shù)型編程語(yǔ)言中的概念,可以方便組合函數(shù),返回函數(shù))的副作用。
??? “動(dòng)態(tài)類型”和“隱式類型”的區(qū)別:一種情況是,靠編譯器推斷出類型,但編譯器在編譯期就推斷出來(lái)了。另一種情況是,編譯器在編譯時(shí)一無(wú)所知,它假設(shè)是任何類型,然后在運(yùn)行時(shí)的適當(dāng)時(shí)機(jī),檢查到底是什么類型。因?yàn)樵谀J(rèn)情況下推斷本地變量聲明的類型,所以無(wú)論 Option Strict 的設(shè)置為何,總是早期綁定對(duì)于這些變量的訪問(wèn)。在 Visual Basic 9.0 中,程序員必須按以下方式將變量顯式聲明為類型 Object,從而顯式指定晚期綁定:要求使用顯式晚期綁定是為了防止意外使用晚期綁定,但更重要的是,這樣做可以有力地?cái)U(kuò)展它對(duì)諸如 XML 等新數(shù)據(jù)類型的晚期綁定。
?????? 新的 Visual Basic 9.0 對(duì)象初始值設(shè)定項(xiàng) 采用的是一種基于表達(dá)式的 With 格式,用于以簡(jiǎn)明的方式創(chuàng)建復(fù)雜的對(duì)象實(shí)例。通過(guò)對(duì)象初始值設(shè)定項(xiàng),我們能夠?qū)⒁陨蟽蓷l語(yǔ)句捕獲為一條(隱式類型的)本地聲明。這種在表達(dá)式中進(jìn)行的對(duì)象初始化對(duì)于查詢而言非常重要。一條查詢語(yǔ)句通常就像由等號(hào)右側(cè)的 Select 子句初始化的對(duì)象聲明。由于 Select 子句返回一個(gè)表達(dá)式,因此我們必須能夠以一條表達(dá)式初始化整個(gè)對(duì)象。任何支持 Add 方法的集合都能夠通過(guò)一個(gè)集合初始值設(shè)定項(xiàng) 表達(dá)式進(jìn)行初始化。
動(dòng)態(tài)語(yǔ)言今年來(lái)發(fā)展很快,各種腳本語(yǔ)言多少都帶有些動(dòng)態(tài)語(yǔ)言的特點(diǎn)。動(dòng)態(tài)語(yǔ)言以其靈活的語(yǔ)法和豐富的運(yùn)行時(shí)行為贏得了許多人的青睞。在.NET陣營(yíng),C#一直扮演著靜態(tài)語(yǔ)言的角色,盡管3.0的語(yǔ)法改變很激進(jìn),但仍注重于編譯時(shí)的類型檢查和約束。動(dòng)態(tài)特性用的不好會(huì)產(chǎn)生更多運(yùn)行時(shí)問(wèn)題,不易確保程序的可靠性,但其提高開發(fā)效率的作用是毋庸置疑的。Visual Basic 9.0是Visual Studio中唯一能讓你統(tǒng)領(lǐng)動(dòng)態(tài)和靜態(tài)兩大領(lǐng)域的語(yǔ)言,讓你在程序中自由選擇喜歡的風(fēng)格。
注意在:=符號(hào)之前的大括號(hào),表示對(duì)參數(shù)名稱的動(dòng)態(tài)結(jié)合。有了動(dòng)態(tài)標(biāo)識(shí)符這一特性,Visual Basic 9.0將不需要利用反射的復(fù)雜語(yǔ)法,就可以做很多需要反射和運(yùn)行時(shí)類型信息來(lái)做的任務(wù),這使得Visual Basic成為解決你手邊小問(wèn)題的最佳幫手。
?????? 在隱式類型的局部變量聲明中,局部變量的類型是通過(guò)局部聲明語(yǔ)句右側(cè)的初始值設(shè)定項(xiàng)表達(dá)式推斷的。推斷類型可防止意外使用后期綁定,更重要的是,它允許為新數(shù)據(jù)類型(如 XML)綁定強(qiáng)大擴(kuò)展。局部變量類型推測(cè):不要擔(dān)心性能問(wèn)題;它是編譯時(shí)由編譯器進(jìn)行推測(cè)的,是一種強(qiáng)類型的特性。 同時(shí),VB 9還支持For(For Each)的循環(huán)變量推測(cè),無(wú)需再臨時(shí)定義循環(huán)變量。
?????? 后期綁定:若將一個(gè)變量聲明為 As Object 或 As Variant(包括 As Form 或 As Control 的變量),Visual Basic 在編譯時(shí)就無(wú)法確定該變量將引用哪種類型的對(duì)象。因而,Visual Basic 必需使用后期綁定,在運(yùn)行時(shí)確定對(duì)象的屬性和方法能否使用該變量。
?????? 若使用后期綁定,則每次調(diào)用屬性或方法時(shí),Visual Basic 都要將成員名傳給該對(duì)象 IDispatch 接口的 GetIDsOfNames 方法。GetIDsOfNames 返回該成員的派遣 ID,或 DispID。Visual Basic 再將該 DispID 傳給 IDispatch 接口的 Invoke 方法來(lái)調(diào)用該成員。
?????? 前期綁定:要是在編譯時(shí) Visual Basic 能夠知道屬性或方法所屬的對(duì)象,就可以預(yù)先查找該成員在類型庫(kù)中的 DispID 或 vtable 地址。這樣就無(wú)須在運(yùn)行時(shí)調(diào)用 GetIDsOfNames。
?????? 當(dāng)顯式聲明了變量的類時(shí),例如 As Widget,該變量就只能存放該類的對(duì)象的引用。Visual Basic 就可認(rèn)為該變量調(diào)用的所有屬性和方法使用前期綁定。
?????? 建議在 Visual Basic 和應(yīng)用程序中使用這種方法來(lái)聲明對(duì)象變量。
?????? 使用前期綁定還是后期綁定完全取決于聲明變量的方式。對(duì)象的樹立方式對(duì)此沒(méi)有任何影響。
?????? 后期綁定的幾個(gè)優(yōu)勢(shì)和好處
第一,個(gè)人以為,由于VB6不支持實(shí)現(xiàn)繼承,對(duì)于某些設(shè)計(jì),可以使用將對(duì)象定義為Variant變量,而獲取某些設(shè)計(jì)和編碼上的便利,而得到接口或抽象類的好處。這樣,無(wú)須繼承機(jī)制,也可以得到抽象接口的優(yōu)勢(shì),而使接口、實(shí)現(xiàn)分離。
第二,在調(diào)用外部的COM自動(dòng)化組件時(shí),如Office,要是通過(guò)這種方式調(diào)用,對(duì)于不同版本的Office,要是你調(diào)用的方法、屬性都是存在的,那么可以適應(yīng)不同版本的Office,但要是通過(guò)前期綁定,在項(xiàng)目引用中確定對(duì)象類型,是有版本兼容問(wèn)題的。
效率和劣勢(shì)
?????? 其實(shí)最主要的劣勢(shì)是效率問(wèn)題,《Advanced Visual Basic 6》一書說(shuō),使用Variant變量和Long變量進(jìn)行循環(huán),效率差別是1.5倍,而沒(méi)有他人以為的那么大,而且對(duì)于大對(duì)象,差異也不是很大。對(duì)于本文前邊的例子,要是建立的是進(jìn)程內(nèi)對(duì)象,前期綁定和后期綁定調(diào)用效率區(qū)別大概2:3左右,也就是說(shuō)也是1.5倍。對(duì)于調(diào)用Office這樣的外部COM自動(dòng)化對(duì)象,也許區(qū)別大一些,沒(méi)有專門測(cè)試,不過(guò)對(duì)于Office自動(dòng)化,速度主如果決定于Office本身的啟動(dòng)、初始化。
?????? 另外一個(gè)劣勢(shì)是沒(méi)有了IDE下的屬性、方法成員自動(dòng)列出。
?????? 早期綁定聯(lián)系由編譯器在根據(jù).NET源 代碼創(chuàng)建程序集的期間創(chuàng)建。我們無(wú)法為一個(gè)虛方法或抽象方法創(chuàng)建一個(gè)早期綁定。事實(shí)上,當(dāng)一個(gè)虛方法或抽象方法被調(diào)用時(shí),多態(tài)機(jī)制會(huì)在執(zhí)行期間根據(jù)被調(diào)用 方法的實(shí)際對(duì)象確定將要執(zhí)行的代碼。這種情況下,該聯(lián)系被視為動(dòng)態(tài)綁定。在其他資料中,動(dòng)態(tài)綁定有時(shí)被稱為隱式的后期綁定,因?yàn)樗鼈兪怯啥鄳B(tài)機(jī)制隱式創(chuàng)建 的并且是在執(zhí)行期完成的。
?????? 現(xiàn)在讓我們進(jìn)一步觀察早期綁定,它們是為靜態(tài)方法或類中那些不是虛方法或抽象方法的方法創(chuàng)建的。如果嚴(yán)格遵循前一節(jié)中對(duì)“類的聯(lián)系”的定義,.NET中是不存在早期綁定的。事實(shí)上,我們必須先等待JIT編譯器將方法體轉(zhuǎn)換為機(jī)器語(yǔ)言,才能知道它在進(jìn)程地址空間中的物理地址。創(chuàng)建程序集的編譯器并不知道這個(gè)方法的地址信息[1]。為了解決這個(gè)問(wèn)題,創(chuàng)建程序集的編譯器在IL代碼中方法將被調(diào)用的位置插入了與被調(diào)用的方法相對(duì)應(yīng)的元數(shù)據(jù)符號(hào)(metadata token) 。當(dāng)方法體被即時(shí)(JIT)編譯的時(shí)候,CLR在內(nèi)部保存了方法與機(jī)器語(yǔ)言下的方法體的物理地址的對(duì)應(yīng)聯(lián)系。這段被稱為存根的信息被物理保存到一個(gè)與方法相關(guān)的內(nèi)存地址中。
?????? 以上的認(rèn)識(shí)很重要,因?yàn)樵谙馛++這樣的語(yǔ)言中,當(dāng)一個(gè)方法不是虛方法或抽象方法(即C++中的純虛函數(shù))時(shí),編譯器就可以計(jì)算出該方法體在機(jī)器語(yǔ)言下的物理地址。然后,編譯器在每個(gè)調(diào)用該方法的位置插入一個(gè)指向該內(nèi)存地址的指針。這個(gè)區(qū)別給了.NET很大的優(yōu)勢(shì),因?yàn)榫幾g器不需要再考慮諸如內(nèi)存表現(xiàn)之類的技術(shù)細(xì)節(jié)。IL代碼完全獨(dú)立于它所運(yùn)行的物理層。
?????? 而在動(dòng)態(tài)綁定中,其中幾乎所有的事物都是以與早期綁定中相同的方式工作的。編譯器在IL代碼中方法被調(diào)用的位置插入了與被調(diào)用的虛(或抽象)方法相對(duì)應(yīng)的元數(shù)據(jù)符號(hào)。這里,我們提到的元數(shù)據(jù)符號(hào)是屬于定義在引用類型中的方法的,而該引用類型就是將發(fā)生方法調(diào)用的那個(gè)類型。然后就是CLR的工作,它將在執(zhí)行期間根據(jù)引用對(duì)象的具體實(shí)現(xiàn)確定跳轉(zhuǎn)到哪個(gè)方法。
?????? 最常見的原因在于某些語(yǔ)言根本就沒(méi)有編譯器!在一個(gè)腳本語(yǔ)言中,指令是被一條一條解釋的。在這種情況下,只存在后期綁定。通過(guò)使用后期綁定,可以使用由解釋型語(yǔ)言編譯的程序集中的類。在.NET中可以方便地使用后期綁定技術(shù)這一事實(shí),使得創(chuàng)建一個(gè)專有的解釋/動(dòng)態(tài)語(yǔ)言變得相對(duì)容易(比如IronPython語(yǔ)言http://www.ironpython.com/)。
我們可能希望在由編譯型語(yǔ)言如C#寫成的程序中使用后期綁定技術(shù)。原因在于,使用后期綁定可以為應(yīng)用程序的通用架構(gòu)帶來(lái)某種程度的靈活性。該技術(shù)實(shí)際上是一種最近很流行的被稱為插件的設(shè)計(jì)模式,我們將在本章對(duì)它做進(jìn)一步介紹。
如果在程序集A編譯期間程序集B尚不存在,我們就必須在A中的代碼與B中的類之間使用后期綁定。這種情況我們將在稍后談到動(dòng)態(tài)構(gòu)造程序集的時(shí)候介紹。
?????? 一些人喜歡使用后期綁定來(lái)代替多態(tài)。事實(shí)上,因?yàn)樵谡{(diào)用期間,只考慮方法的名稱與簽名式,而與被調(diào)用方法所處對(duì)象的類型無(wú)關(guān),所以只需要在實(shí)現(xiàn)對(duì)象的時(shí)候提 供具有合適名稱和簽名式的方法即可。但是,我個(gè)人不推薦這種做法,因?yàn)樗募s束性太差,并且無(wú)法促使應(yīng)用程序的開發(fā)者去做恰當(dāng)?shù)脑O(shè)計(jì)以及使用抽象接口。
轉(zhuǎn)載于:https://www.cnblogs.com/brunoyu/archive/2009/07/18/1526124.html
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的一次公司内部的Tech Talk中涉及到的关于语言的发展问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何:修改 Office Open XM
- 下一篇: SQL Server 2008 阻止保存