上回咱們談到了components,這回咱們就來聊聊這components倒底是怎麼一回事。
上回咱們談到了components,這回咱們就來聊聊這components倒底是怎麼一回事。
從Dispose談起
在建立自訂類別時通常都會建議要實做 IDisposable 介面,使得該類別能具備Dispose方法 (當然你也可以不需要Implements IDisposable也能寫出這方法來,只要記得起來如何正確實做哪些成員即可,老實講我是完全記不起來的啦),Dispose方法的實做提供類別能夠在使用完畢後標註自己的狀態為可回收,使得CLR能夠在適當時機將這些動態記憶體釋放出來。
關於Dispose,在點部落上有一些朋友嘔心瀝血的作品可以參考:
Top Cat:物件Object的New,Dispose與Connection的Open,Close概念分享
Rico:[C#][Tips]Dispose是否影響Connection Pooling?
Jeff Yeh:DB Connection 的Close與Dispose
在MSDN文件庫中有個章節 [記憶體回收的基本概念] 說明了CLR的記憶體回收基本觀念。
IContainer?
在前文提到了在Designer程式碼中的變數宣告 Private components As System.ComponentModel.IContainer ,這個變數的型別是IContainer;MSDN文件庫描述它是這樣的東西:為容器 (Container) 提供功能。 容器是邏輯上包含零或多個元件的物件。不過其實備註寫的比較精彩 (所以說要看備註啊):
容器是封裝和追蹤零或多個元件的物件。 在本文中,內含項目是指邏輯的而非視覺上的內含項目。 元件和容器可以用於各種案例中,包括視覺的和隱藏式的案例在內。
實作者注意事項
若要成為容器,類別必須實作 IContainer 介面,這個介面支援加入、移除和擷取元件的方法。
講了個半天就是說『所有要成為容器的類別一定要實做這個介面』(同IDisposible介面的說法,如果你真記得起來,沒人非逼你加上一行Implements IContainer不可),所以要成為一個容器至少要具備幾項要求:
- 可以加個物件進來容器
- 可以把一個物件從容器中移除
- 要可以釋放資源,意即要實做Dispose方法
- 要可以取得容器內物件的集合
不知道會不會有人想辯稱說他可以Implements IContainer後,每個方法的內容都是沒有程式碼的,那這樣算不算容器?基本上算,只不過那就是個廢物。
謎底揭曉
瞎扯了這麼多還沒講到正題,應該有人開始罵髒話了,請原諒我常常為了補版面一直瞎掰的習慣。
當我們從設計畫面的工具箱拖拉元件到畫面時,有某些玩意兒不是繼承Control類別而來的,例如說 ImageList、Forms.Timer等直接繼承Component 類別並且在建構函式中有一個多載是可以傳入IContainer型別參數的。例如Forms.Timer的建構函式:
來看一下這個建構函式的備註,真是夠了又是備註:
備註
Timer 建構函式可讓您將 Timer 與任何 Container 物件產生關聯。藉由這個方法與 Timer 產生關聯之後,您就會將 Timer 之存留期的控制權交給 Container。如果您在應用程式中使用一些元件,並且想要同步處置所有元件,那麼這個方法會相當實用。例如,如果您讓 ToolTip、ImageList 和 Timer 與 Container 產生關聯,那麼在 Container 上呼叫 Dispose 同樣會強制處置這些元件。
當建立新的計時器時,它是停用的;也就是說,Enabled 設為 false。若要啟用計時器,請呼叫 Start 方法或是將 Enabled 設為 true。
這個執行個體將會一直存在,直到它的容器釋放到記憶體回收。
我把重點用斜體並改為藍色標明,這也就是說,當我們加入了一堆不會列入Form.Controls屬性中的物件時,如果這些個玩意可以和一個Container物件關聯,當我要Dispose它們的時候,只要Dispose該關聯容器就一次解決了,接著下來我們先新建一個專案,然後從工具列拖拉以下三個元件:Timer、ImageList以及Serialport;在Designer程式碼的底端宣告了三個私有變數,如同加入一般的UI控制項一樣:
Friend WithEvents SerialPort1 As System.IO.Ports.SerialPort
Friend WithEvents Timer1 As System.Windows.Forms.Timer
Friend WithEvents ImageList1 As System.Windows.Forms.ImageList
有趣的事情發生在 Private Sub InitializeComponent() 方法中:
1. 它使用Container類別建構函式為components產生了一個執行個體
Me.components = New System.ComponentModel.Container()
2. 它建構這三個元件都使用傳遞IContainer型別參數的建構函式,並以components物件為引數,使這三個元件與components物件產生關聯
Me.SerialPort1 = New System.IO.Ports.SerialPort(Me.components)
Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
Me.ImageList1 = New System.Windows.Forms.ImageList(Me.components)
3. 也因為這樣使得 Protected Overrides Sub Dispose(ByVal disposing As Boolean) 方法中的
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
成為一段有意義的程式碼
我個人一直覺得寫程式是個重基礎的事情,即使現代的程式開發環境已經進步到如此視覺化的程度,許多的基本道理還是不可以忽視的。拖拖拉拉雖然方便,但追根究底更是樂趣無窮,當我們對於基本道理的瞭解更深一層,在撰寫程式的技巧上也會更進一步,這也就是為什麼點部落這次辦Windows Form修煉活動我會以Windows Form Designer為第一個主題的原因,希望初學者不僅能瞭解Designer中的程式設計原理,更能夠藉由這樣基礎原理的文章能更瞭解基本技巧的重要性,進而加強對於基本原理的學習與理解,而不再一直誤入網路程式文抄公那種錯誤的方向。