2011-01-21

VB.NET中如何動態指定物件型別

在寫新版OMS發票軟體,其中有個目標功能是希望能加入多種發票機的支援。但是這樣一來,產生發票機物件的時候就會有個問題:要如何從設定檔內動態的指定物件的型別?
我把發票機的控制寫在一個class內,這個Class內用SerialPort實作發票機的控制與一些自訂的函數,然後由主程式產生這個物件來做列印。但日後有可能會需要支援多種發票機,假設有A和B兩種,我會想每種發票機個寫一個Class來控制,然後依照使用者的設定來選擇要產生發票機的型別(假設Class名稱是WPA與WPB),但如果照著底下寫法,會因為在IF這個區塊裡產生的,屬於區域變數,無法在IF外使用:
IF Config("PrinterModel") = "A" Then
   Dim Printer as New WPA("COM1")
Else
    Dim Printer as New WPB("COM1")
End If
Printer.Print("...") '這行一定無法執行
如果是寫在Form_Load的外面,也因為需要先指定物件的型別而只能寫死無法依照使用者的選擇來指定不同的型別。在藍色小舖上詢問過後,大致上可以有以下三種解決方式:



by Allen

1. 您先寫一個介面(interface)或父類別
2. 再寫WPA,WPB,WPC,…去實作上述介面或繼承上述父類別
3. 在config檔裡設定好要建立的物件,例如
4. 然後,參考
將它建立出來, 並轉型成第一點裡面的介面或父類別即可

by 叛逆之風

建議實作 Allen 大所說的,日後比較好管理和維護。
或是參考以下方法
1 . 先將物件變數宣告為Object在全域區段 =NOTHING
2 . 在 IF 內再去 NEW 物件
3 . 呼叫物件前先判斷 If IsNot Nothing Then
4 . 缺點,使用物件前請記得轉型 CType()
Public Class Form1
    Dim Printer As Object = Nothing
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Config As String = "A"
        If Config = "A" Then
            Printer = New Label
            CType(Printer, Label).Name = "Label"
        Else
            Printer = New TextBox
            CType(Printer, TextBox).Name = "TextBox"
        End If
        If Printer IsNot Nothing Then
            MsgBox(Printer.name)
        End If
    End Sub
End Class

by 史努比

提供您一些小技巧, 先把WPA和WPB 二個類別萃取共同點另外做一個抽象類別出來(好比列印工作的處理流程, 二種印表機都是相同的)
把不一樣的東西做成抽象方法交給次類別來處理(好比印表機的各種控制碼), 最後在抽象類別裡放入靜態方法作為工廠方法, 傳回印表機物件
Form 中如果需要印表機, 可以直接使用 Dim Printer AS InvoicePrinter = InvoicePrinter.GetPrinter(Config("PrinterModel"))
這樣的語法即可, 千萬不要每個FORM都寫一個Select Case, 這樣的做法以後再擴充其它印表機時, 您就知道痛苦了…..
Public MustInherits Class InvoicePrinter
    Public shared Function GetPrinter(Byval PrinterModel AS String) AS InvoicePrinter
        Select Case PrinterModel
           Case "A"
                Return New WPA("Com1")
           Case "B"
                Return New WPB("Com1")
        End Select
    End Function

    Public Function Print(Byval Data AS String()) AS Boolean
        '.... 這裡放上處理文字列印的流程
        '....
    End Function

    Public MustOverride Function GetControlCode(Byval Command AS PrinterCommandType) AS String '用抽象方法把一些工作交給次類別處理
End Class

Public Class WPA
    Inherits InvoicePrinter
'..... '放入WPA印表機的控制碼
End Class

Public Class WPB
    Inherits InvoicePrinter
'..... '放入WPB印表機的控制碼 
End Class
其中第三點就是我之前有想過的方法,但一直卡在,就算是抽象類別InvoicePrinter好了,我在實際產生物件的時候,還是需要Dim printer as New InvoicePrinter,這樣還是沒辦法取得到底該用哪一個型別,而史努比所提出的解決方式正好解決了我的疑惑!這也正是我所需要的!