逐步解說:在 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",且不會移除前置和後端空格。 動態類別的預設行為是在每一行開頭搜尋相符項目,並移除前置和後端空格。
若要建立自訂動態類別
啟動 Visual Studio。
選取 [建立新專案]。
在 [建立新專案] 對話方塊中,選取 [Visual Basic] 和 [主控台應用程式],然後選取 [下一步]。
在 [設定新專案] 對話方塊中,輸入
DynamicSample
作為 [專案名稱],然後選取 [下一步]。在 [其他資訊]對話方塊中,對於 [目標框架]選取 [.NET 5.0 (目前)],然後選取 [建立]。
隨即建立新專案。
在 [方案總管] 中,以滑鼠右鍵按一下 DynamicSample 專案,然後選取 [新增>類別]。 在 [名稱] 方塊中,輸入
ReadOnlyFile
,然後選取 [新增]。隨即新增檔案,其中包含 ReadOnlyFile 類別。
在 ReadOnlyFile.cs 或 ReadOnlyFile.vb 檔案頂端,新增下列程式碼以匯入 System.IO 和 System.Dynamic 命名空間。
Imports System.IO Imports System.Dynamic
自訂動態物件會使用列舉,來判斷搜尋準則。 在 class 陳述式之前,加入下列列舉定義。
Public Enum StringSearchOption StartsWith Contains EndsWith End Enum
如下列程式碼範例所示,更新繼承
DynamicObject
類別的 class 陳述式。Public Class ReadOnlyFile Inherits DynamicObject
將下列程式碼新增至
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
將下列
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
在
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
在
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
儲存並關閉檔案。
若要建立範例文字檔
在 [方案總管] 中,以滑鼠右鍵按一下 DynamicSample 專案,然後選取 [新增]>[新項目]。 在 [已安裝的範本] 窗格中,選取 [一般],然後選取 [文字檔] 範本。 保留 [名稱] 方塊中的 [TextFile1.txt] 預設名稱,然後按一下 [新增]。 新的文字檔隨即加入專案中。
將下列文字複製到 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
儲存並關閉檔案。
若要建立使用自訂動態物件的範例應用程式
在 [方案總管] 中,按兩下 [Program.vb] 檔案。
將下列程序碼新增至
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
儲存檔案,並按下 Ctrl+F5 以組建並執行應用程式。
呼叫動態語言程式庫
下一個逐步解說將建立專案,該專案可存取以動態語言 IronPython 寫入的程式庫。
若要建立自訂動態類別
在 Visual Studio 中,選取 [檔案]>[新增]>[專案]。
在 [建立新專案] 對話方塊中,選取 [Visual Basic] 和 [主控台應用程式],然後選取 [下一步]。
在 [設定新專案] 對話方塊中,輸入
DynamicIronPythonSample
作為 [專案名稱],然後選取 [下一步]。在 [其他資訊]對話方塊中,對於 [目標框架]選取 [.NET 5.0 (目前)],然後選取 [建立]。
隨即建立新專案。
安裝 IronPython NuGet 封裝。
編輯 Program.vb 檔案。
在檔案頂端,新增下列程式碼以匯入來自 IronPython 程式庫和
System.Linq
命名空間的Microsoft.Scripting.Hosting
和IronPython.Hosting
命名空間。Imports Microsoft.Scripting.Hosting Imports IronPython.Hosting Imports System.Linq
在 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.")
在程式碼載入 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
儲存檔案,並按下 Ctrl+F5 以組建並執行應用程式。