對IDisposable和靜態分析的提議:DisposeUnused屬性

當 .NET初創的時候,關於IDisposable該如何使用存在一定的不確定性。結果,IDisposable的應用方式過於激進,許多種類的類都需要空的Dispose方法。這給靜態分析工具帶來了一些問題,它們無法將實際缺少Dispose調用與誤報區分開來。

爲了理解始末緣由,我們需要回過頭看一下CLR早期的歷史以及垃圾收集是如何運行的。最初,CLR的目的在於是作爲Visual Basic的新運行時,在20世紀90年代末Visual Basic是基於COM的。在COM模型下,對象會有一個引用計數。在引用創建和銷燬的時候,引用計數會隨之進行更新,如果計數變成零,對象就會被釋放。這樣的話,就形成了一個具有確定性的垃圾收集模型,在這種模型中,我們可以確切地知道該在什麼時候清理資源。

引用方式的垃圾收集模型的最明顯缺點就在於它很容易出現內存泄露。如果我們創建了一系列的對象,它們之間互相循環引用的話,每個對象都會讓其他對象的引用計數無法降低到零,從而會出現內存泄露。在多線程環境中,它還會產生性能問題,因爲在調整引用計數的時候,需要用到鎖。

在開發的早期,微軟決定採用標記-清理(mark-and-sweep)垃圾收集器來讓CLR避免這些問題。隨着Java的流行,這種方式在社區中得到了普遍的認可。但是,這種方式的GC並不能確定地釋放資源,這使得它不適合用於數據庫連接、文件處理和其他高度受限的資源。因此,IDisposable應運而生。

與此同時,微軟正在試驗“組件”的理念。組件的概念從來沒有被很好地定義。在這方面,有Component類,以及像IComponentIContainerISite這樣的接口。在將近20年之後,文檔中也只有一個很模糊的註釋:“應用程序之間的對象共享”。其想法大概和COM類似,也就是某個應用可以直接與其他程序進行交互。但是,這並沒有達到目的,所以被埋沒在歷史的故紙堆中了。

而在Windows Forms中,有一個不同的“組件”概念,它真正的意思是“可以放到表單/窗口(form/window)中的內容”。除了像文本框這樣的實際UI元素之外,還包括添加了特定功能的對象,如計時器。這來自VB 6編程時代,當時幾乎所有想使用的內容都必須要放到表單中。甚至數據庫連接和命令也可以直接放到表單中。

這也就是爲什麼我們看到很多毫不相關的對象也標記成了IDisposable。像DataTableSqlCommand並沒有要處理的非託管資源,但是在過去我們錯誤地認爲最好將它們放到表單中,所以它們繼承了Component類。而Component是disposable的,所以我們可以選擇何時關閉代理對象。

靜態分析

隨着靜態分析逐漸從高級工具變成了每個開發人員都該使用的工具,關於disposable對象不斷增長的告警越來越成爲一個問題。對於像SqlCommand這樣短期存活的對象來說,這還不算太糟糕,因爲可以很容易地使用using語句對其進行包裝,而不需要考慮該語句實際上並沒有執行任何操作。

DataTable這樣的類就比較困難了。這是一個長期存活的對象,它所使用的地方可能距離創建它的地方非常遠。除非設置爲suppressed或禁用,否則靜態分析工具將會報告DataTable和類似對象有未處理處理的告警和錯誤。

DisposeUnusedAttribute提議

“最佳”方案是徹底移除所有無用的Dispose方法。但是,這並不是可行方案,因爲這樣會破壞向後的兼容性。

Edward Brey提出了一個相當簡單而優雅的解決方案。他建議創建一個DisposeUnused屬性來屏蔽靜態分析工具。子類不會繼承此屬性。

但是,這個設計也並非盡善盡美。一旦DisposeUnused用到了某個類上,移除它將會是破壞性的變更。對於DataTable來說,這並不是什麼問題,不過,Stephen A. Imhoff提供一個這樣的樣例。

這實際上會更糟糕,因爲現在你可能會說“好的,忽略該契約,我就是這樣聲明的”,如果某個類型突然需要dispose某個資源的話(比如說,MemoryStream要爲大型數組或其他內容分配一個原生數組),那麼你的消費者需要執行dispose操作,但是你之前卻告訴人家不需要這樣做……

另一個問題是你並不是總能知道變量裏有什麼。假設有一個類型爲Component的變量。在編譯時,我們無法判斷放入變量的內容是否需要dispose處理。因此,更有意義的做法是隻將DisposeUnused用到密閉類(sealed classed)中,這些類是無法子類化的。

原文鏈接:
A Proposal for IDisposable and Static Analysis: DisposeUnused Attribute

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章