2010-11-30 52 views
13

在.NET引用類型數組中是協變量。這被認爲是一個錯誤。不過,我不明白爲什麼這麼糟糕請考慮下面的代碼:爲什麼陣列協方差被認爲如此可怕?

string[] strings = new []{"Hey there"}; 
object[] objects = strings; 
objects[0] = new object(); 

哦,這個編譯並會在運行時失敗。正如我們試圖將一個對象粘貼到一個字符串[]中。好吧,我同意,很臭,但T []數組延伸,也實現了IList(和IList<T>,我不知道如果它實現IList<BaseType> ...>。這兩個數組和IList的允許我們犯同樣的錯誤可怕。

string[] strings = new []{"Hey there"}; 
Array objects = strings; 
objects.SetValue(new object(),new[]{0}); 

IList的版本

string[] strings = new []{"Hey there"}; 
IList objects = strings; 
objects[0] = new object(); 

的T []類由CLR生成,並且必須包括在set_Item方法是等效的類型檢查(陣列實際上沒有一個)。

是擔心設置爲T []必須在運行時進行類型檢查(這違反了編譯時期望的類型安全性)?爲什麼在有相同的手段通過上面提供的手段在腳下射擊自己時,陣列展示這種屬性會有害嗎?

+0

你是不是指對象[0] = new object(); ? – 2010-11-30 19:07:28

+0

是的,趕上! – 2010-11-30 19:09:52

回答

13

是的,IListArray允許你犯同樣的錯誤 - 因爲他們是弱類型的API開始。

陣列看起來像他們強烈類型(在編譯時),但實際上他們不是。他們可能如此容易安全(和更快),但他們不是。這只是一個性能和編譯時安全性的浪費機會:(

+0

抖動不能優化出類型檢查嗎? – 2010-11-30 19:14:54

0

數組方差的一個代價是對非密封參考類型數組的賦值有點貴,但考慮到對引用類型的賦值已經做了很多相關的GC一點我想這成本不顯著。

22

在.NET中引用類型數組共變的。這被認爲是一個錯誤。

類型安全打破陣列協方差被認爲是有些人是.NET設計中的一個錯誤。這不是所有人都這麼認爲的。我不認爲它是一個錯誤;我認爲這是一個不幸的選擇。所有設計過程都涉及不良替代品之間的選擇。在這種情況下,選擇是在添加不安全的隱式轉換之間進行,該轉換會在所有陣列寫入上施加運行時開銷,或者構建無法輕鬆實現Java類型系統的類型系統。這是一個艱難的選擇,類型系統的設計者根據他們所擁有的信息做出了最好的選擇。

當然這個解釋僅僅是問題乞求;那不就是Java的設計者犯了錯誤嗎?可能是的,可能不是; Java的設計人員也可能在其類型系統的設計中面臨折衷。任何關於Java類型系統開發歷史的專家都想在這裏介紹一下這些權衡之處,我很想知道。我認爲,如果.NET類型系統的設計者選擇避開安全破壞的數組協方差,那麼我認爲,如果有十年的事後認識,我個人會更喜歡它。但是這並不能使這個選擇成爲一個「錯誤」,這隻會讓它有點不幸。

設置爲T []的設置是否需要在運行時進行類型檢查(這違反了編譯時期望的類型安全性)?

是。這意味着看起來應該總是成功運行的代碼在運行時可能會失敗。這意味着正確的代碼會對其施加性能損失。

爲什麼數組在展示這個屬性的時候認爲是有害的?當有相同的方法通過上面提供的方法在腳上拍攝自己?

這是一個奇怪的問題。這個問題基本上是「我已經有兩把槍,我可以用腳射我自己,那麼爲什麼我認爲在第三腳用自己的腳射傷自己是有害的?」

存在兩種違反類型安全的危險模式並不會使第三種模式的危險性降低。

違反類型安全性的語言和運行時功能在那些您絕對肯定知道您所做的事情是安全的時候,即使編譯器不知道它。如果您不能很好地理解這些功能以便安全地使用它們,請不要使用它們。

1

我認爲你的筆記IList指向這裏值得考慮的事情。

IList由數組實現是非常有用的。實現它的其他集合也很有用。

現在,在過去的5年中,我們經常發現它更有用(或同樣有用和更安全)來處理IList<T>。我們沒有,我們只有IList。在很多情況下,可能會在數組和其他集合之間移動(在最好情況下)泛型之前,在許多情況下,我們可以更加自信地在類型化集合和類型化數組之間移動。

因此,在做出相關決定時,支持協變陣列的論據比現在更多。而當他們沒有泛型的時候,他們在Java中做出類似的決定只會增加這個事實。