轉帖|其它|編輯:郝浩|2010-06-17 11:54:05.000|閱讀 829 次
概述:如果用戶需要的是Windows.Forms里面的數十種控件,那么你的Select語句也要寫數十行嗎?若有一種方法能夠在運行時任意指定對象的創建類型,甚至是用表示類型的名字的字符串創建所需的對象,該有多么方便。.net Framwork的反射機制給我們帶來了解決問題的方法。這里,若只需要創建一般的對象,我們可以通過System.Activator來實現,而較復雜的我們可以通過獲取構造方法來實現。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
看到標題,大部分會說“運行時創建對象”那不是小兒科,就這樣:
Dim newButton As Button = New Button()
newButton.Name = "Button1"
這的確是在運行時創建了一個按鈕。不過若需按照用戶要求創建按鈕、復選框或者單選框怎么辦,好像也好辦:
Dim newControl As Control
Select Case userSelection
Case "按鈕"
newControl = New Button()
Case "復選框"
newControl = New CheckBox()
....
End Select
如果用戶需要的是Windows.Forms里面的數十種控件,那么你的Select語句也要寫數十行嗎?我當然不是想要做這種刁難的用戶,但是需求總是多種多樣的,若有一種方法能夠在運行時任意指定對象的創建類型,甚至是用表示類型的名字的字符串創建所需的對象,該有多么方便。.net Framwork的反射機制給我們帶來了解決問題的方法。這里,若只需要創建一般的對象,我們可以通過System.Activator來實現,而較復雜的我們可以通過獲取構造方法來實現。
反射Reflection是.net中重要機制,很多人已經介紹過反射,我們來簡單復習一下。通過反射,可以在運行時獲得.net中每一個類型(包括類、結構、委派、接口、枚舉)的成員,包括方法、屬性、事件以及構造函數等,還可以獲得每個成員的名稱、限定符和參數等,有了反射,就可以對每一個類型了如指掌。如果獲得了構造函數的信息,就可以直接創建對象,即使這個對象的類型在編譯的時候還不知道。
在完成運行時創建控件這一任務前,我們先看一個簡單的例子,建立一個名為VBAppliction的Windows程序,添加一個新文件,輸入一個新類:
Public Class MyClassTest
Private MyField As String
Public Sub New()
MyField = "Hi!"
End Sub
Public Sub Hello()
Console.WriteLine(MyField)
End Sub
End Class
然后加給窗體入一個新按鈕,輸入以下事件代碼:
''方法一
Dim t As Type = GetType(MyClassTest)
o = System.Activator.CreateInstance(t)
o.Hello()
第一行GetType(MyClassTest)函數就已經獲得了我們創建的類的類型對象(C#中使用typeof函數)。接下來,我們用了System.Activator類的一個靜態方法CreateInstance創建出對象實例,并將對象引用賦給o。Activator是一個用來在創建本地或遠程對象的工具。運行這個程序,我們可以從Commond Window(命令窗口,一般在調試狀態IDE的右下方)看到WriteLine函數運行的結果,可以看到正確建立的對象。
如果我們用的類具有比較復雜的構造函數,還可以使用構造函數創建所需的對象,代碼如下:
''方法二
Dim t As Type = GetType(MyClassTest)
Dim c As System.Reflection.ConstructorInfo
Dim types() As Type
ReDim types(-1)
c = t.GetConstructor(Reflection.BindingFlags.Instance _
Or Reflection.BindingFlags.Public, _
Nothing, Reflection.CallingConventions.HasThis, types, Nothing)
Dim params() As Object
ReDim params(-1)
o.Hello()
這里我們創建一個System.Reflection.ConstructorInfo的對象,通過它可以獲得類構造方法的信息。我們用的是Type類的GetConstructor方法來搜索可用的構造方法。
需要解釋一下的是types()數組,這個數組是搜索構造方法所用的參數類型表。我們的類的構造方法沒有參數,所以需要一個空但不為Nothing(C#中為null)的數組,ReDim types(-1)就是建立這種數組的語句,在C#中可寫作:
types = new Type[0];
若構造方法是這樣:
Public Sub New(ByVal A As Integer, B As String)
那么相應的types數組就應該是
Dim types(1) As Type
types(0) = GetType(Int32)
types(1) = GetType(String)
Reflection.BindingFlags.Instance和Reflection.BindingFlags.Public是一個位屏蔽,是指定搜索方式的選項。
params()數組是構造方法的參數內容表,同樣因沒有參數,我們使用ReDim -1的語法。
Invoke方法執行了構造方法,創建出對象實例。
現在我們回到第一種實現方法,將代碼改一下,將
Dim t As Type = GetType(MyClassTest)
改為
Dim t As Type = Type.GetType("VBApplication.MyClassTest")
運行的結果沒有改變,這就是說,我們實現了從字符串創建對象!不過這里GetType方法的使用有限制,具體我們后面再說。現在就可以實現我們的愿望:動態創建控件。通過上面的知識,我們很容易寫出一個動態創建窗口控件的子程序:
Private Function CreateNewControls(ByVal targetControls As Control.ControlCollection, ByVal ctlName As String, ByVal ctlType As Type, ByVal ctlSize As Drawing.Size, ByVal ctlLocation As Drawing.Point) As Control
Dim toCreate As Control
toCreate = CType(System.Activator.CreateInstance(ctlType), Control)
toCreate.Name = ctlName
toCreate.Size = ctlSize
toCreate.Location = ctlLocation
targetControls.Add(toCreate)
Return toCreate
End Function
那一句較長的語句中包含了上一個例子中的所有內容。如果用C#書寫,則可以寫成
toCreate = (Control)System.Activator.CreateInstance(ctlType);
我們將按鈕的事件過程改成:
Dim c As Control = Me.CreateNewControls1(Me.Controls, "Control1", GetType(CheckBox), New Size(168, 40), New Point(64, 176))
c.Text = "New Creation"
現在,單擊一下按鈕,就可以看到一個新的CheckBox出現在窗口上,標題為New Creation,而且,如果編寫了事件過程,還可以為新建的控件添加事件響應。
看來一切都達到目的了?注意這一句GetType(CheckBox)還是使用了類名的字面表示,無法達到用字符串創建對象的功能。如果我們把這一句改成Type.GetType("System.Windows.Forms.CheckBox")行不行?嗯,試驗一下,呵呵,出錯了。為什么會這樣?Type.GetType()方法從字符串獲得類型僅限于corlib中的類型或者工程內部的類型,如果是來自于外部的程序集就需要加以程序集的名稱。Windows.Forms程序集是公有的程序集,是位于程序集緩存中的,可以在.net Framwork內部實現side by side執行。所以這個程序集有不同的版本,為了確定使用的版本,我們不僅要提供程序集的名稱,還要提供程序集的版本和強名稱。按照這個思路,在我使用的.net Framework 1.1上,將這一句寫成Type.GetType("System.Windows.Forms.CheckBox, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")。現在運行就沒有問題了。問題是我們如何取得所用Windows.Forms程序集的版本和強名稱?可以用GetType(CheckBox).AssemblyQualifiedName這樣的語法,一旦得到了這些信息,我們就可以將這些信息用于其它任何控件,因為他們都來自于同一個版本Windows.Forms程序集。現在可以來玩一個好玩的,放一個文本框到窗口上,比如叫做TextBox1,將按鈕的事件過程改為:
Try
Dim c As Control = Me.CreateNewControls1(Me.Controls, "Control1", Type.GetType("System.Windows.Forms." & TextBox1.Text & ", System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"), New Size(168, 40), New Point(64, 176))
c.Text = "New Creation"
Catch ex As Exception
MsgBox(ex.Message)
End Try
現在只要在TextBox1種輸入“Button”,按下按鈕,一個新按鈕產生了!如果輸入的是CheckBox,那么將產生一個復選框。現在無論用戶怎樣刁難,控件都能正確“按需創建”了。反射機制在.net中還有很多用途,據說Delphi.net中的類引用及虛擬構造函數等功能用于.net Framwork時就是借助于反射及System.Type類型實現的,善用這一利器會給你的程序增色不少。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:慧都控件網