共用方式為


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

動態物件會在運行時間公開屬性和方法等成員,而不是在編譯時期公開。 動態物件允許您建立物件,以操作與靜態類型或格式不匹配的結構。 例如,您可以使用動態對象來參考 HTML 檔物件模型 (DOM),其中包含有效 HTML 標記元素和屬性的任何組合。 因為每個 HTML 檔都是唯一的,因此特定 HTML 檔的成員會在運行時間決定。 參考 HTML 元素屬性的常見方法是將屬性的名稱傳遞至 GetProperty 專案的方法。 若要引用 HTML 元素 id<div id="Div1"> 屬性,請先取得 <div> 元素的參考,然後使用 divElement.GetProperty("id")。 如果您使用動態物件,可以將 屬性參考 iddivElement.id

動態物件也可讓您輕鬆存取動態語言,例如 IronPython 和 IronRuby。 您可以使用動態物件來參考在運行時間解譯的動態腳本。

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

您可以使用 命名空間中的 System.Dynamic 類別來建立自訂動態物件。 例如,您可以在運行時間建立 ExpandoObject 並指定該對象的成員。 您也可以建立一個類型,此類型繼承DynamicObject 類別。 然後,您可以覆寫 DynamicObject 類別的成員,以提供動態的運行時功能。

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

  • 建立自定義物件,以動態方式將文本文件的內容公開為 對象的屬性。
  • 建立使用 IronPython 連結庫的專案。

先決條件

  • 已安裝 .NET 桌面開發工作負載的Visual Studio 2022 17.3版或更新版本。 當您選取此工作負載時,會包含 .NET 7 SDK。

備註

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

建立自定義動態物件

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

建立自定義動態類別

啟動 Visual Studio。 選取 [建立新專案]。 在 [ 建立新專案] 對話框中,選取 [C#],選取 [ 控制台應用程式],然後選取 [ 下一步]。 在 [ 設定新專案 ] 對話框中,輸入 DynamicSample[項目名稱],然後選取 [ 下一步]。 在 其他資訊 對話框中,選取 目標框架.NET 7.0(目前),然後選取 建立。 在 [方案總管] 中,以滑鼠右鍵按兩下 DynamicSample 項目,然後選取 [新增>類別]。 在 [ 名稱] 方塊中,輸入 ReadOnlyFile,然後選取 [ 新增]。 在 ReadOnlyFile.csReadOnlyFile.vb 檔案頂端,新增下列程式代碼以匯入 System.IOSystem.Dynamic 命名空間。

using System.IO;
using System.Dynamic;

自定義動態物件會使用列舉來判斷搜尋準則。 在類別語句之前,新增下列列舉定義。

public enum StringSearchOption
{
    StartsWith,
    Contains,
    EndsWith
}

更新 class 語句以繼承 DynamicObject 類別,如下列程式代碼範例所示。

class ReadOnlyFile : DynamicObject

將下列程式代碼新增至 ReadOnlyFile 類別,以定義檔案路徑的私人字段和 類別的 ReadOnlyFile 建構函式。

// Store the path to the file and the initial line count value.
private string p_filePath;

// Public constructor. Verify that file exists and store the path in
// the private variable.
public ReadOnlyFile(string filePath)
{
    if (!File.Exists(filePath))
    {
        throw new Exception("File path does not exist.");
    }

    p_filePath = filePath;
}
  1. 將下列 GetPropertyValue 方法新增至 ReadOnlyFile 類別。 此方法 GetPropertyValue 以搜尋準則作為輸入,並返回符合該搜尋準則的文本檔行。 類別所提供的 ReadOnlyFile 動態方法會呼叫 GetPropertyValue 方法,以擷取其各自的結果。
public List<string> GetPropertyValue(string propertyName,
                                     StringSearchOption StringSearchOption = StringSearchOption.StartsWith,
                                     bool trimSpaces = true)
{
    StreamReader sr = null;
    List<string> results = new List<string>();
    string line = "";
    string testLine = "";

    try
    {
        sr = new StreamReader(p_filePath);

        while (!sr.EndOfStream)
        {
            line = sr.ReadLine();

            // Perform a case-insensitive search by using the specified search options.
            testLine = line.ToUpper();
            if (trimSpaces) { testLine = testLine.Trim(); }

            switch (StringSearchOption)
            {
                case StringSearchOption.StartsWith:
                    if (testLine.StartsWith(propertyName.ToUpper())) { results.Add(line); }
                    break;
                case StringSearchOption.Contains:
                    if (testLine.Contains(propertyName.ToUpper())) { results.Add(line); }
                    break;
                case StringSearchOption.EndsWith:
                    if (testLine.EndsWith(propertyName.ToUpper())) { results.Add(line); }
                    break;
            }
        }
    }
    catch
    {
        // Trap any exception that occurs in reading the file and return null.
        results = null;
    }
    finally
    {
        if (sr != null) {sr.Close();}
    }

    return results;
}

GetPropertyValue 方法之後,新增下列程式碼以重寫 TryGetMember 類別的 DynamicObject 方法。 當動態類別的成員被要求且未指定任何自變數時,就會呼叫TryGetMember方法。 binder 參數包含有關參照的成員的信息,result 參數參照指定成員所傳回的結果。 方法 TryGetMember 會傳回 true 布爾值,如果要求的成員存在,則傳回 ,否則會傳 false回 。

// Implement the TryGetMember method of the DynamicObject class for dynamic member calls.
public override bool TryGetMember(GetMemberBinder binder,
                                  out object result)
{
    result = GetPropertyValue(binder.Name);
    return result == null ? false : true;
}

TryGetMember 方法之後,新增下列程式碼以重寫 TryInvokeMember 類別的 DynamicObject 方法。 當使用帶引數請求動態類別的成員時,會呼叫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 override bool TryInvokeMember(InvokeMemberBinder binder,
                                     object[] args,
                                     out object result)
{
    StringSearchOption StringSearchOption = StringSearchOption.StartsWith;
    bool trimSpaces = true;

    try
    {
        if (args.Length > 0) { StringSearchOption = (StringSearchOption)args[0]; }
    }
    catch
    {
        throw new ArgumentException("StringSearchOption argument must be a StringSearchOption enum value.");
    }

    try
    {
        if (args.Length > 1) { trimSpaces = (bool)args[1]; }
    }
    catch
    {
        throw new ArgumentException("trimSpaces argument must be a Boolean value.");
    }

    result = GetPropertyValue(binder.Name, StringSearchOption, trimSpaces);

    return result == null ? false : true;
}

儲存並關閉檔案。

建立範例文字檔

[方案總管] 中,以滑鼠右鍵按兩下 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.cs 檔案。 將下列程式代碼新增至 Main 程式,以建立 ReadOnlyFile 檔案的 類別實例。 程序代碼會使用晚期系結來呼叫動態成員,並擷取包含字串 「Customer」 的文字行。

dynamic rFile = new ReadOnlyFile(@"..\..\..\TextFile1.txt");
foreach (string line in rFile.Customer)
{
    Console.WriteLine(line);
}
Console.WriteLine("----------------------------");
foreach (string line in rFile.Customer(StringSearchOption.Contains, true))
{
    Console.WriteLine(line);
}

儲存檔案,並按下 Ctrl+F5 以組建並執行應用程式。

呼叫動態語言庫

下列逐步解說會建立一個專案,來存取以動態語言 IronPython 撰寫的函式庫。

建立自定義動態類別

在 Visual Studio 中,選取 檔案>新增>專案。 在 [ 建立新專案] 對話框中,選取 [C#],選取 [ 控制台應用程式],然後選取 [ 下一步]。 在 [ 設定新專案 ] 對話框中,輸入 DynamicIronPythonSample[項目名稱],然後選取 [ 下一步]。 在 其他資訊 對話框中,選取 目標框架.NET 7.0(目前),然後選取 建立。 安裝 IronPython NuGet 套件。 編輯 Program.cs 檔案。 在檔案頂端,新增下列程式碼以匯入 IronPython 庫中的 Microsoft.Scripting.HostingIronPython.Hosting 命名空間,還有 System.Linq 命名空間。

using System.Linq;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;

在Main方法中,新增下列程式代碼以建立新的 Microsoft.Scripting.Hosting.ScriptRuntime 對象來裝載 IronPython 連結庫。 物件 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");
ScriptRuntime py = Python.CreateRuntime();
dynamic random = py.UseFile("random.py");
Console.WriteLine("random.py loaded.");

載入 random.py 模組的程式代碼之後,新增下列程式代碼以建立整數陣列。 陣列會傳遞至 shuffle random.py 模組的 方法,以隨機排序陣列中的值。

// Initialize an enumerable set of integers.
int[] items = Enumerable.Range(1, 7).ToArray();

// Randomly shuffle the array of integers by using IronPython.
for (int i = 0; i < 5; i++)
{
    random.shuffle(items);
    foreach (int item in items)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("-------------------");
}

儲存檔案,並按下 Ctrl+F5 以組建並執行應用程式。

另請參閱