.net中實現運行時從字串動態建立對象

來源:互聯網
上載者:User

本文轉自:http://www.cnblogs.com/godwar/archive/2008/01/17/1042481.html

 

看到標題,大部分會說“運行時建立對象”那不是小兒科,就這樣:
  
  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類型實現的,善用這一利器會給你的程式增色不少。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.