共用方式為


逐步解說:在 Visual Basic 中建立和使用動態物件

動態物件會在執行階段公開成員 (例如屬性和方法),而不是在編譯時期。 這可讓您建立物件,以使用與靜態類型或格式不相符的結構。 例如,您可以使用動態物件來參考 HTML 文件物件模型 (DOM),其可包含任何有效的 HTML 標記項目和屬性組合。 由於每個 HTML 文件都是唯一的,因此系統會在執行階段決定特定的 HTML 文件成員。 參考 HTML 項目屬性的常用方法,是將屬性名稱傳遞給項目的 GetProperty 方法。 若要參考 HTML 項目 <div id="Div1">id 屬性,您要先取得 <div> 項目的參考,然後再使用 divElement.GetProperty("id")。 如果您使用動態物件,則可以 divElement.id 的形式來參考 id 屬性。

動態物件也可讓您方便存取動態語言 (諸如 IronPython 和 IronRuby)。 您可以使用動態物件,參考在執行階段受到解譯的動態指令碼。

您可以使用晚期繫結,參考動態物件。 您可將晚期繫結物件的型別指定為 Object。 如需詳細資訊,請參閱 [早期和晚期繫結

您可以在 System.Dynamic 命名空間中使用類別,以建立自訂動態物件。 例如,您可以建立 ExpandoObject,並在執行階段中指定該物件的成員。 您也可以建立繼承 DynamicObject 類別的專屬類型。 接著,您即可覆寫 DynamicObject 類別的成員,以提供執行階段動態功能。

本文包含兩個獨立的逐步解說:

  • 建立自訂物件,以將文字檔內容動態公開為物件的屬性。

  • 建立專案,以使用 IronPython 程式庫。

您可以執行其中一項或兩項,而如果您欲兩項都執行,則順序並不重要。

必要條件

  • 已安裝 .NET 桌面開發工作負載的 Visual Studio 2019 16.9 版或更新版本。 您選取此工作負載時,即會自動安裝 .NET 5 SDK。

注意

在下列指示的某些 Visual Studio 使用者介面項目中,您的電腦可能會顯示不同的名稱或位置: 您所擁有的 Visual Studio 版本以及使用的設定會決定這些項目。 如需詳細資訊,請參閱將 Visual Studio IDE 個人化

  • 針對第二個逐步解說,請安裝 IronPython for .NET。 請移至下載頁面以取得最新版本。

建立自訂的動態物件

第一個逐步解說定義了可搜尋文字檔內容的自訂動態物件。 動態屬性會指定要搜尋的文字。 例如,如果呼叫程式碼指定 dynamicFile.Sample,動態類別會傳回字串的泛型清單,其中包含檔案中開頭為 "Sample" 的所有行。 搜尋不區分大小寫。 動態類別也支援兩個選擇性引數。 第一個引數是搜尋選項列舉值,其指定動態類別應該在行開頭、行結尾或行的任何位置,搜尋相符項目。 第二個引數指定動態類別應該先修剪文字的前置和後端空格,再進行搜尋。 例如,如果呼叫程式碼指定 dynamicFile.Sample(StringSearchOption.Contains),動態類別即會在行的任何位置搜尋 "Sample"。 如果呼叫程式碼指定 dynamicFile.Sample(StringSearchOption.StartsWith, false),則動態類別會在每一行開頭搜尋 "Sample",且不會移除前置和後端空格。 動態類別的預設行為是在每一行開頭搜尋相符項目,並移除前置和後端空格。

若要建立自訂動態類別

  1. 啟動 Visual Studio。

  2. 選取 [建立新專案]

  3. 在 [建立新專案] 對話方塊中,選取 [Visual Basic] 和 [主控台應用程式],然後選取 [下一步]

  4. 在 [設定新專案] 對話方塊中,輸入 DynamicSample 作為 [專案名稱],然後選取 [下一步]

  5. 在 [其他資訊]對話方塊中,對於 [目標框架]選取 [.NET 5.0 (目前)],然後選取 [建立]

    隨即建立新專案。

  6. 在 [方案總管] 中,以滑鼠右鍵按一下 DynamicSample 專案,然後選取 [新增>類別]。 在 [名稱] 方塊中,輸入 ReadOnlyFile,然後選取 [新增]

    隨即新增檔案,其中包含 ReadOnlyFile 類別。

  7. ReadOnlyFile.csReadOnlyFile.vb 檔案頂端,新增下列程式碼以匯入 System.IOSystem.Dynamic 命名空間。

    Imports System.IO
    Imports System.Dynamic
    
  8. 自訂動態物件會使用列舉,來判斷搜尋準則。 在 class 陳述式之前,加入下列列舉定義。

    Public Enum StringSearchOption
        StartsWith
        Contains
        EndsWith
    End Enum
    
  9. 如下列程式碼範例所示,更新繼承 DynamicObject 類別的 class 陳述式。

    Public Class ReadOnlyFile
        Inherits DynamicObject
    
  10. 將下列程式碼新增至 ReadOnlyFile 類別,以定義檔案路徑的私用欄位和 ReadOnlyFile 類別的建構函式。

    ' Store the path to the file and the initial line count value.
    Private p_filePath As String
    
    ' Public constructor. Verify that file exists and store the path in 
    ' the private variable.
    Public Sub New(ByVal filePath As String)
        If Not File.Exists(filePath) Then
            Throw New Exception("File path does not exist.")
        End If
    
        p_filePath = filePath
    End Sub
    
  11. 將下列 GetPropertyValue 方法新增至 ReadOnlyFile 類別。 GetPropertyValue 方法會以輸入形式採用搜尋準則,並傳回文字檔中符合搜尋條件的幾行內容。 ReadOnlyFile 類別所提供的動態方法會呼叫 GetPropertyValue 方法,以擷取其各自的結果。

    Public Function GetPropertyValue(ByVal propertyName As String,
                                     Optional ByVal StringSearchOption As StringSearchOption = StringSearchOption.StartsWith,
                                     Optional ByVal trimSpaces As Boolean = True) As List(Of String)
    
        Dim sr As StreamReader = Nothing
        Dim results As New List(Of String)
        Dim line = ""
        Dim testLine = ""
    
        Try
            sr = New StreamReader(p_filePath)
    
            While Not sr.EndOfStream
                line = sr.ReadLine()
    
                ' Perform a case-insensitive search by using the specified search options.
                testLine = UCase(line)
                If trimSpaces Then testLine = Trim(testLine)
    
                Select Case StringSearchOption
                    Case StringSearchOption.StartsWith
                        If testLine.StartsWith(UCase(propertyName)) Then results.Add(line)
                    Case StringSearchOption.Contains
                        If testLine.Contains(UCase(propertyName)) Then results.Add(line)
                    Case StringSearchOption.EndsWith
                        If testLine.EndsWith(UCase(propertyName)) Then results.Add(line)
                End Select
            End While
        Catch
            ' Trap any exception that occurs in reading the file and return Nothing.
            results = Nothing
        Finally
            If sr IsNot Nothing Then sr.Close()
        End Try
    
        Return results
    End Function
    
  12. GetPropertyValue 方法後面,新增下面程式碼以覆寫 DynamicObject 類別的 TryGetMember 方法。 如果系統要求動態類別的成員,但未指定任何參數,則會呼叫 TryGetMember 方法。 binder 引數包含參考成員的相關資訊,而 result 引數會參考指定成員傳回的結果。 TryGetMember 方法會傳回布林值:如果要求的成員存在,該值會傳回 true;否則會傳回 false

    ' Implement the TryGetMember method of the DynamicObject class for dynamic member calls.
    Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
                                           ByRef result As Object) As Boolean
        result = GetPropertyValue(binder.Name)
        Return If(result Is Nothing, False, True)
    End Function
    
  13. TryGetMember 方法後面,新增下面程式碼以覆寫 DynamicObject 類別的 TryInvokeMember 方法。 如果系統要求具有引數的動態類別成員,則會呼叫 TryInvokeMember 方法。 binder 引數包含參考成員的相關資訊,而 result 引數會參考指定成員傳回的結果。 args 引數包含傳遞給該成員的引數陣列。 TryInvokeMember 方法會傳回布林值:如果要求的成員存在,該值會傳回 true;否則會傳回 false

    自訂版 TryInvokeMember 方法需要的第一個引數,是來自上一個步驟所定義的 StringSearchOption 列舉。 TryInvokeMember 方法需要的第二個引數是布林值。 如果這兩個引數有一個或兩個是有效的值,即會將其傳遞給 GetPropertyValue 方法以擷取結果。

    ' Implement the TryInvokeMember method of the DynamicObject class for 
    ' dynamic member calls that have arguments.
    Public Overrides Function TryInvokeMember(ByVal binder As InvokeMemberBinder,
                                              ByVal args() As Object,
                                              ByRef result As Object) As Boolean
    
        Dim StringSearchOption As StringSearchOption = StringSearchOption.StartsWith
        Dim trimSpaces = True
    
        Try
            If args.Length > 0 Then StringSearchOption = CType(args(0), StringSearchOption)
        Catch
            Throw New ArgumentException("StringSearchOption argument must be a StringSearchOption enum value.")
        End Try
    
        Try
            If args.Length > 1 Then trimSpaces = CType(args(1), Boolean)
        Catch
            Throw New ArgumentException("trimSpaces argument must be a Boolean value.")
        End Try
    
        result = GetPropertyValue(binder.Name, StringSearchOption, trimSpaces)
    
        Return If(result Is Nothing, False, True)
    End Function
    
  14. 儲存並關閉檔案。

若要建立範例文字檔

  1. 在 [方案總管] 中,以滑鼠右鍵按一下 DynamicSample 專案,然後選取 [新增]>[新項目]。 在 [已安裝的範本] 窗格中,選取 [一般],然後選取 [文字檔] 範本。 保留 [名稱] 方塊中的 [TextFile1.txt] 預設名稱,然後按一下 [新增]。 新的文字檔隨即加入專案中。

  2. 將下列文字複製到 TextFile1.txt 檔案。

    List of customers and suppliers
    
    Supplier: Lucerne Publishing (https://www.lucernepublishing.com/)
    Customer: Preston, Chris
    Customer: Hines, Patrick
    Customer: Cameron, Maria
    Supplier: Graphic Design Institute (https://www.graphicdesigninstitute.com/)
    Supplier: Fabrikam, Inc. (https://www.fabrikam.com/)
    Customer: Seubert, Roxanne
    Supplier: Proseware, Inc. (http://www.proseware.com/)
    Customer: Adolphi, Stephan
    Customer: Koch, Paul
    
  3. 儲存並關閉檔案。

若要建立使用自訂動態物件的範例應用程式

  1. 在 [方案總管] 中,按兩下 [Program.vb] 檔案。

  2. 將下列程序碼新增至 Main 程序,以針對 TextFile1.txt 檔案建立 ReadOnlyFile 類別的執行個體。 程式碼會使用晚期繫結呼叫動態成員,並擷取包含字串 "Customer" 的文字行。

    Dim rFile As Object = New ReadOnlyFile("..\..\..\TextFile1.txt")
    For Each line In rFile.Customer
        Console.WriteLine(line)
    Next
    Console.WriteLine("----------------------------")
    For Each line In rFile.Customer(StringSearchOption.Contains, True)
        Console.WriteLine(line)
    Next
    
  3. 儲存檔案,並按下 Ctrl+F5 以組建並執行應用程式。

呼叫動態語言程式庫

下一個逐步解說將建立專案,該專案可存取以動態語言 IronPython 寫入的程式庫。

若要建立自訂動態類別

  1. 在 Visual Studio 中,選取 [檔案]>[新增]>[專案]

  2. 在 [建立新專案] 對話方塊中,選取 [Visual Basic] 和 [主控台應用程式],然後選取 [下一步]

  3. 在 [設定新專案] 對話方塊中,輸入 DynamicIronPythonSample 作為 [專案名稱],然後選取 [下一步]

  4. 在 [其他資訊]對話方塊中,對於 [目標框架]選取 [.NET 5.0 (目前)],然後選取 [建立]

    隨即建立新專案。

  5. 安裝 IronPython NuGet 封裝。

  6. 編輯 Program.vb 檔案。

  7. 在檔案頂端,新增下列程式碼以匯入來自 IronPython 程式庫和 System.Linq 命名空間的 Microsoft.Scripting.HostingIronPython.Hosting 命名空間。

    Imports Microsoft.Scripting.Hosting
    Imports IronPython.Hosting
    Imports System.Linq
    
  8. 在 Main 方法中加入下列程式碼,以建立裝載 IronPython 程式庫的新 Microsoft.Scripting.Hosting.ScriptRuntime 物件。 ScriptRuntime 物件會載入 IronPython 程式庫模組 random.py。

    ' Set the current directory to the IronPython libraries.
    System.IO.Directory.SetCurrentDirectory(
        Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) &
           "\IronPython 2.7\Lib")
    
    ' Create an instance of the random.py IronPython library.
    Console.WriteLine("Loading random.py")
    Dim py = Python.CreateRuntime()
    Dim random As Object = py.UseFile("random.py")
    Console.WriteLine("random.py loaded.")
    
  9. 在程式碼載入 random.py 模組之後,請加入下列程式碼以建立整數陣列。 系統會將陣列傳遞給 random.py 模組的 shuffle 方法,其會隨機排序陣列中的值。

    ' Initialize an enumerable set of integers.
    Dim items = Enumerable.Range(1, 7).ToArray()
    
    ' Randomly shuffle the array of integers by using IronPython.
    For i = 0 To 4
        random.shuffle(items)
        For Each item In items
            Console.WriteLine(item)
        Next
        Console.WriteLine("-------------------")
    Next
    
  10. 儲存檔案,並按下 Ctrl+F5 以組建並執行應用程式。

另請參閱