麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發 > Java > 正文

C#、Java以及C++的泛型

2024-07-21 02:26:57
字體:
來源:轉載
供稿:網友

摘要

anders hejlsberg,c#的主架構師,與bruce eckel和bill venners 談論了c#和java的泛型、c++模板、c#的constraints特性以及弱類型化和強類型化的問題。

anders hejlsberg,微軟的一位杰出工程師,他領導了c#(發音是c sharp)編程語言的設計團隊。hejlsberg首次躍上軟件業界舞臺是源于他在80年代早期為ms-dos和cp/m寫的一個pascal編譯器。不久一個叫做borland的非常年輕的公司雇傭了他并且買下了他的編譯器,從那以后這個編譯器就作為turbo pascal在市場上推廣。在borland,hejlsberg繼續開發turbo pacal并且在后來領導一個團隊設計turbo pascal的替代品:delphi。1996年,在borland工作13年以后,hejlsberg加入了微軟,在那里一開始作為visual j++和windows基礎類庫(wfc)的架構師。隨后,hejlsberg擔任了c#的主要設計者和.net框架創建過程中的一個主要參與者。現在,anders hejlsberg領導c#編程語言的后續開發。

2003年7月30號,bruce eckel(《thinking in c++》以及《thinking in java》的作者)和bill venners(artima.com的主編)與anders hejlsberg在他位于華盛頓州redmond的微軟辦公室進行了一次面談。這次訪談的內容將分多次發布在artima.com以及bruce eckel將于今年秋天發布的一張音頻光碟上。在這次訪談中,anders hejlsberg談論了c#語言和.net框架設計上的一些取舍。

·        在 第一部分:c#的設計過程中, hejlsberg談論了c#設計團隊所采用的流程,以及在語言設計中可用性研究(usability studies)和好的品味(good taste)相對而言的優點。

·        在第二部分:checked exceptions的問題中, hejlsberg談論了已檢測異常(checked exceptions)的版本(versionability)問題和規模擴展(scalability)問題。

·        在第三部分: 委托、組件以及表面上的簡單性里,hejlsberg 談論了委托(delegates)以及c#對于組件的概念給予的頭等待遇。

·        在第四部分:版本,虛函數和覆寫里,hejlsberg解釋了談論了為什么c#的方法默認是非虛函數,以及為什么程序員必須顯式指定覆寫(override)。

在第五部分:契約和互操作性里,hejlsberg談論了dll hell、接口契約、strong anmes以及互操作的重要性。
在第六部分:inappropriate abstractions里, hejlsberg以及c#團隊的其他成員談論了試圖讓網絡透明的分布式系統,以及試圖屏蔽掉數據庫的對象——關系映射。  
在第七部分, hejlsberg比較了c#和java的泛型以及c++模板的實現方法,并且介紹了c#的constraints特性以及弱類型化和強類型化的問題。
泛型概述
bruce eckel: 能否就泛型做一個簡短的介紹?

anders hejlsberg: 泛型的本質就是讓你的類型能夠擁有類型參數。它們也被成為參數化類型(parameterized types)或者參數的多態(parametric polymorphism)。經典的例子十九一個list集合類。list是一個方便易用的、可增長的數組。它有一個排序方法,你可以通過索引來引用它的元素,等等。現今,如果沒有參數化類型,在使用數組或者lists之間就會有些別扭的地方。如果使用數組,你得到了強類型保證,因為你可以定義一個關于customer的數組,但是你沒有可增長性和那些方便易用的方法。如果你用的是list,雖然你得到了所有這些方便,但是卻喪失了強類型保證。你不能指定一個list是關于什么的list。它只是一個關于object的list。這會給你帶來一些問題。類型檢測必須在運行時刻做,也就意味著沒有在編譯時刻對類型進行檢測。即便是你塞給list一個customer對象然后試圖取出一個string,編譯器也不會有絲毫的抱怨。直到運行時刻你才會發現他會出問題。另外,當把基元類型(primitive type)放入list的時候,還必須對它們進行裝箱(box)。基于上述所有這些問題,lists與arrays之間的這種不和諧的地方總是存在的。到底選擇哪個,會讓你一直猶豫不決。

泛型的最大好處就是它讓你有了一個兩全其美的辦法(you can have your cake and eat it too),因為你可以定義一個list<t>[讀作:list of t]。當使用一個list的時候,你可以實實在在地知道這個list是關于什么類型的list,并且讓編譯器為你做強類型檢測。這只是它最直接的好處。接下來還有其它各種各樣的好處。當然,你不會僅僅想讓list擁有泛型。哈希表(hashtable)或者字典(dictionary)——隨便你怎么叫它——把鍵(keys)映射到值(values)。你可能會想要把strings映射到customrs,或者ints到orders,而且是以強類型化的方式。

c#的泛型
bill venners: 泛型在c#中是如何工作的?

anders hejlsberg: 沒有泛型的c#,基本上你只能寫class list {...}。有了泛型,你可以寫成class list<t> {...},這里t是類型參數。在list<t>范圍內你可以把t當作類型來使用,當真正需要創建一個list對象的時候,寫成list<int>或者list<customer>。新類型是通過list<t>構建的,實際上就像是你的類型參數替換掉了原本的類型參數。所有的t都變成了ints或者customers,你不需要做類型轉換,因為到處都會做強類型檢驗。

在clr(common language runtime)環境下,當編譯list<t>或者其它任何generic類型的時候,會像其它普通類型一樣,先編譯成中間語言il(intermediate language)以及元數據。理所當然,il以及元數據包含了額外的信息,從而可以知道有一個類型參數,但是從原則上來說,generic類型的編譯與其它類型并沒有什么不同。在運行時刻,當應用程序第一次引用到list<int>的時候,系統會查找看是否有人已經請求過list<int>。如果沒有,它會把list<t>的il和元數據以及類型參數int傳遞給jit。而jiter在即時編譯il的過程中,也會替換掉類型參數。

bruce eckel: 也就是說它是在運行時刻實例化的。

anders hejlsberg: 的確如此,它是在運行時刻實例化的。它在需要的時候產生出針對特定類型的原生代碼(native code)。從字面上看,當你說list<int>的時候,你會得到一個關于int的list。如果generic類型的代碼使用了一個關于t的array,你得到的就是一個關于int的array。

bruce eckel: 垃圾回收機制會在某個時候來回收它么?

anders hejlsberg: 可以說會,也可以說不會,這是一個正交的問題。這個類在應用程序范圍內被創建,然后在這個應用程序范圍內就一直存在下去。如果你殺掉這個應用程序,那么這個類也就消失了,這點跟其它類一樣。

bruce eckel: 如果我有一個應用程序用到了list<int>和list<cat>,但是它從來沒有走到使用list<cat>的那個分支。。。。。。

anders hejlsberg:。。。。。。那么系統就不會實例化一個list<cat>。現在讓我說說一些例外的情況。如果你是使用ngen在創建一個影像(image),也就是說你在直接產生一個native的映像,你可以提早產生這些實例。但是如果你是在通常的情況下運行程序,是否實例化是完全根據需要來確定的,而且推遲到越晚越好。

這之后,我們針對所有值類型(比如list<int>,list<long>,list<double>, list<float>)的實例化做進一步的處理,創建可執行的原生代碼的唯一拷貝。這樣list<int>就有它自己的代碼。list<long>也有它自己的代碼。list<float>也是如此。對于所有引用類型(reference types),我們共享這些代碼,因為它們所代表的東西是相同的。它們只是一些指針罷了。

bruce eckel: 你需要進行類型轉換吧。

anders hejlsberg: 不,實際上并不需要。我們可以共享native image,但實際上它們有各自單獨的虛函數表(vtables)。我只是想指出,當共享代碼有意義的時候,我們會不遺余力的去做這件事情,但是當你非常需要運行效率的時候,我們對于共享代碼會非常謹慎。通常對于值類型,你確實會關心list<int>元素的類型就是int。你不想把它們裝箱(box)成objects。對值類型進行裝箱/拆箱,是可以用來進行代碼共享的一種方法,但是這種方法代價過于昂貴。

bill venners: 對于引用類型,實際上也是完全不同的類。list<elephant>和list<orangutan>是不同的,但是它們確實共享所有的類方法的代碼。

anders hejlsberg: 是的。作為實現上的細節來說,它們確實共享了相同的原生代碼(native code)。

c#泛型與java泛型的比較
bruce eckel: c#泛型相比java泛型有什么特點?

anders hejlsberg: java的泛型實現是基于一個最初叫做pizza的項目,這個項目是由martin odersky和其他一些人完成的。pizza被重新命名為gj,然后他成了一個jsr,并且最后被采納進了java語言。這個特定的泛型proposal有一個關鍵的設計目標,就是它應該能夠跑在不必經過改動的虛擬機上。不用改動虛擬機當然很棒,但是它也帶來了一系列奇奇怪怪的限制。這些限制并不都是顯而易見的,但是很快你就會說,“hmm,這可有點怪。”

比如說,使用java泛型,實際上你就得不到任何剛才我所說得程序執行上的效率,因為當你在java里編譯一個泛型類的時候,編譯器拿掉了類型參數并到處代之以object。list<t>編譯好的影像文件(image)就像是一個到處使用object(作為類型參數)的list。當然,如果你試圖創建一個list<int>,那就的對所有用到的int對象進行裝箱(boxing)。這就產生了很大的負擔。此外,為了與老的虛擬機兼容,編譯器實際上會插入各種各樣的轉換代碼,而這些轉換代碼并不是由你來寫的。如果是一個關于object的list,而你試圖把這些objects當作customers來對待,這些objects必須在某些地方被轉換成customers,以便讓verifier的驗證能夠通過。實際上它們的實現所做的就是自動為你插入那些類型轉換。也就是說你得到了語法上的甜頭,或者至少是一部分語法上的甜頭,但是你并沒有得到任何程序執行上的效率。這是我認為java泛型解決方案的第一個問題。

第二個問題是,我認為這可能是更大的一個問題,因為java的泛型實現依賴于去處掉類型參數,當到了運行時刻,你實際上并沒有一個相對于運行時刻的可靠的泛型表示。當你在java里針對一個泛型list使用反射(reflection)的時候,你并不知道這個list到底是關于什么的list。它只是一個list。因為你已經丟失了類型信息,對于任何動態代碼生成(dynamic code-generation)的應用或者基于反射的應用,就沒法工作了。這種趨勢對我來說已經很明了了,(丟失類型信息的)情況越來越多。它根本沒辦法工作,因為你丟失了類型信息。而在我們的實現里,所有這些信息都是可獲得的。你可以通過反射得到list<t>對象的system.type表示。但這時候你還不能創建它的實例,因為你還不知道t是什么。但是你可以使用反射得到int的system.type表示。然后你可以請求反射機制把這兩個東西放在一起創建一個list<int>,這樣你就得到了另外一個用以表示list<int>的system.type。也就是說,從表示方法來說,任何你可以在編譯時刻做到的事情,你也可以在運行時刻做到。

c#泛型與c++模板的比較
bruce eckel: c#泛型相比c++模板有哪些特點?

anders hejlsberg: 在我看來,理解c#泛型與c++模板之間的差異最重要的一點就是:c#泛型實際上就像是類,除了它們有類型參數。而c++模板實際上就像是宏(macros),除了它們看起來像是類。

c#泛型與c++模板最大的不同之處在于類型檢驗發生的時間以及實例化的方式。首先,c#是在運行時刻實例化的,而c++ 是在編譯時刻或者可能是在link的時候。但是不管怎樣,c++模板實例化發生在程序運行之前。這是第一個不同之處。第二個不同之處在于,當你編譯generic類型的時候,c#對它進行強類型檢驗。對于像list<t>這樣未加限制的類型參數(unconstrained type parameter),類型t的值所能使用的方法僅限于object類型所包含的方法,因為只有這些方法才是通常我們保證能夠存在的方法。也就是說,在c#泛型里,我們保證你所實施于類型參數的任何操作都會成功。

c++正好與此相反。在c++里,你可以對一個類型參數做任何你想做的事情。但是當你對它進行實例化的時候,它有可能通不過,而你會得到一些非常難懂的錯誤信息。比如,你有一個類型參數t以及兩個t類型的變量,x和y,如果你寫成x+y,那你最好事先定義了用于兩個t型變量相加的+運算符,否則你會得到一些古怪的錯誤信息。所以從某種意義上說,c++模板實際上是非類型化的,或者說是弱類型化的。而c#泛型則是強類型化的。

c#泛型的constraints特性
bruce eckel: constraints在c#泛型里是如何工作的?

anders hejlsberg: 在c#泛型里,我們可以針對類型參數加一些限制條件(constraints)。還以list<t>為例,你可以寫成,class list<t> where t: icomparable。意思是t必須實現icomparable接口。

bruce eckel: 有意思的是在c++里限制條件是隱含的。

anders hejlsberg: 是的。在c#里,你也可以讓限制條件是隱含的。比如說我們有一個dictionary<k,v>,它有一個add方法,以k為鍵(key)v為值(value)。add方法的實現很可能需要把傳入的鍵與dictionary已有的鍵進行比較,而且它可能通過一個叫做icomparable的接口來做這個比較。一種方法是把key參數轉換成icomparable,然后調用compareto方法。當然,當你這么做的時候,你就已經針對k類型和key參數創建了一個隱式的限制條件。如果傳入的key沒有實現icomparable接口,你就會得到一個運行時錯誤。但是實際上你并沒有在你的哪個方法里或者約定里明確表明key必須實現icomparable。而且你當然還得付出運行時刻類型檢測的代價,因為實際上你所做的是運行時刻的動態類型檢驗。

使用constraint,你可以把代碼里的動態檢驗提前,在編譯時刻或者加載的時候對它進行驗證。當你指定k必須實現icomparable,這就隱含了一系列的東西。對于任何k類型的值,你都可以直接訪問接口方法,而不需要進行轉換,因為從語義上來說,在整個程序里k類型要實現這個接口,這一點是得到保證的。無論什么時候你想要創建該類型的一個實例,編譯器都會針對你給出的任何作為k參數的類型進行檢驗,看它是否實現了icomparable。如果沒有實現,你會得到一個編譯時錯誤。或者如果你是利用反射來做的話,會得到一個異常。

bruce eckel: 你說到了編譯器以及運行時刻。

anders hejlsberg: 編譯器會做檢驗,但是你也可能是在運行時刻通過反射來做的,這時候就由系統來做檢驗。如前所述,任何你在編譯時刻可以做的事情,你都可以在運行時刻通過反射來做。

bruce eckel: 我是否可以寫一個模板函數,或者換句話說,一個參數類型未知的函數?你們是在所做的是給容器加上更強的類型檢驗,但是我是否可以像在c++模板里那樣得到弱類型化的東西呢?比如說,我是否可以寫一個函數,它以a a和b b作為參數,然后我在代碼里就可以寫a+b?我是否可以不關心a和b是什么,只要它們有一個“+”運算符就可以了,因為我想要的是弱類型化。

anders hejlsberg: 你實際上問的是,通過constraints你到底能做到什么程度?與其它特性類似,如果把constraints發揮到極致,他可以變得異常復雜。仔細想想,其實constraints是一種模式匹配(pattern matching)的機制。你想要能指定,“該類型參數必須有一個接受兩個參數的構造函數,并且實現了+運算符,要有某個靜態方法,以及其它兩個非靜態方法,等等。”問題是,你想要這種模式匹配的機制復雜到哪種程度?

從什么也不做到功能全面的模式匹配,這是很大的一個范圍。我們認為什么也不做太說不過去了,而全面的模式匹配又會變得非常復雜,所以我們選擇了折衷的方式。我們允許你指定一個constraint,它可以是一個類、零個或者多個接口、以及叫做constructor constraint的東西。比如說,你可以指定“該類型必須實現ifoo和ibar接口,”或者“該類型必須繼承自基類x。”一旦你這么做了,我們會在所有地方做類型檢驗以確認該constraint是否為真,包括編譯時刻和運行時刻。任何由這個constraint所暗含的方法都可以通過類型參數的實例直接訪問。

另外,在c#里,運算符都是靜態成員函數。也就是說,一個運算符永遠不可能成為一個接口的成員函數,因此一個接口限制條件(interface constraint)永遠不可能讓你指定一個“+”運算符。要指定一個“+”運算符,唯一的方法就是通過一個類限制條件(class constraint),這個類限制條件指定說必須繼承自某個類,比如說number類,因為number有一個“+”運算符。但是你不可能把它抽象成:“必須有一個+運算符”,然后由我們來以多態的方式解析它的實際含義。

bill venners: 你是通過類型,而不是簽名(signature)來實現限制條件的。

anders hejlsberg: 是的。

bill venners: 也就是說指定類型必須擴展某個類或者實現某些接口。

anders hejlsberg: 是的。本來我們可以走得更遠。我們確實考慮過走得更遠一些,但是那會非常復雜。并且我們不知道添加這些復雜性相對于你所獲得的微不足道的好處,是否值得。如果你想做的事情沒有被constraint系統直接支持,你可以借助于工廠模式(factory pattern)來完成。比如說,你有一個矩陣類matrix<t>,在這個matrix里你想定義一個標量積(dot product)方法。這當然意味著你最終需要理解如何把兩個t相乘,但你不能把它表達成一個constraint,至少如果t是int、double或者float的時候這樣做不行。但是你可以這么做:讓matrix接受一個calculator<t>這樣的參數,然后在calculator<t>里聲明一個叫做multiply的方法。你實現這個方法并把它傳給matrix。

bruce eckel: calculator也是個參數化類型。

anders hejlsberg: 是的,它有點像factory模式。總之,是有辦法來做這些事情的。可能不如你想要的那么棒,但是任何事情都是有代價的。

bruce eckel: 嗯,我感覺c++模板像是一種弱類型化(weak typing)的機制。當你開始在它上面添加constraints的時候,你是在從弱類型化轉向強類型化(strong typing)。通常加入強類型化都會讓事情更加復雜。這像是一個頻譜。

anders hejlsberg: 你所意識到的類型化(typing)的問題,其實是一個撥盤(dial)。你把它撥的越高,程序員越覺得難受,但同時代碼更安全了。但是在兩個方向上你都有可能把它撥過頭。

 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 中国av免费在线观看 | 亚洲va久久久噜噜噜久久男同 | 91久久夜色精品国产网站 | 一本一本久久a久久精品综合小说 | 成人在线视频免费看 | www国产成人免费观看视频,深夜成人网 | 姑娘第四集免费看视频 | 中韩毛片 | 一级电影免费在线观看 | 精品在线观看一区二区三区 | 中文字幕在线观看视频一区 | 国产精品成人av片免费看最爱 | 亚洲码无人客一区二区三区 | 黄色网页在线看 | 久久人人av| 性大片1000免费看 | 369看片你懂的小视频在线观看 | 欧美日韩中文字幕在线视频 | 一区二区三区四区高清视频 | 日韩一级精品 | 97青青草视频 | 黄色片网站在线免费观看 | 五月婷婷第四色 | 九九热色| 久久亚洲国产精品 | 国产成人高清在线观看 | 中文字幕在线免费观看电影 | 日本中文高清 | 久久国产精品99国产 | 色婷婷一区二区三区 | 欧美激情天堂 | 亚洲综合视频网 | 草操影院| 91成人免费看片 | 羞羞色网站| 中文字幕免费看 | 久久精品欧美一区二区 | 一区www| 99re热精品视频 | 黄色高清视频网站 | 久久电影一区二区 |