首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

C# dynamic 與 XML 互轉實戰:DynamicXMLNode 動態包裝器深度解析

     編輯:關於ASP.NET

C# 4.0 引入 dynamic 關鍵字,配合 DynamicObject 可構建極具表達力的 XML 操作 DSL。本文基於 Alexandra Rusina 的 DynamicXMLNode 實作,深度解析動態成員存取機制與陷阱。

DynamicXMLNode 核心實現

public class DynamicXMLNode : DynamicObject {
    private readonly XElement _node;
    public DynamicXMLNode(string name) : this(new XElement(name)) { }
    public DynamicXMLNode(XElement node) { _node = node; }

    // 寫入屬性/子元素:<Name>Value</Name> 或 @Attr="Value"
    public override bool TrySetMember(SetMemberBinder binder, object value) {
        var name = binder.Name;
        if (value is DynamicXMLNode childNode) {
            _node.Add(childNode._node); // 子元素
        } else {
            _node.SetAttributeValue(name, value); // 屬性
        }
        return true;
    }

    // 讀取子元素:若不存在返回 null → 觸發 RuntimeBinderException
    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        var child = _node.Element(binder.Name);
        if (child != null) {
            result = new DynamicXMLNode(child);
            return true;
        }
        result = null;
        return false; // 關鍵:返回 false 會拋出 RuntimeBinderException
    }

    // 隱式轉換為 XElement 便於互操作
    public static implicit operator XElement(DynamicXMLNode node) => node._node;
}

使用範例與陷阱演示

dynamic contact = new DynamicXMLNode("Contacts");
contact.Name = "Patrick Hines"; // 設定屬性 @Name

// 正確:先建立子節點物件再賦值
contact.Address = new DynamicXMLNode("Address");
contact.Address.Street = "123 Main St"; // <Address><Street>123 Main St</Street></Address>

// 錯誤:直接對不存在的子節點賦值屬性
// contact.Address.Street = "123 Main St"; // RuntimeBinderException: 'Address' 不存在

運行結果截圖

關鍵機制解析

操作觸發方法行為
contact.Name = "X"TrySetMember設定屬性 @Name="X"
contact.Address = new DynamicXMLNode()TrySetMember新增子元素 <Address></Address>
contact.Address.Street (讀取)TryGetMember查找子元素 Street,存在返回包裝器,否則 null + false → 拋異常
contact.Address.Street = "Y" (寫入)TryGetMember → TrySetMember先獲取 Address 包裝器,再對其 TrySetMember("Street", "Y")

進階擴展:支援索引器、動態方法、命名空間

// 索引器:contact.Children[0].Name
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
    int idx = (int)indexes[0];
    var child = _node.Elements().ElementAtOrDefault(idx);
    result = child != null ? new DynamicXMLNode(child) : null;
    return child != null;
}

// 動態方法:contact.AddChild("Phone", "123")
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {
    if (binder.Name == "AddChild" && args.Length == 2) {
        _node.Add(new XElement(args[0].ToString(), args[1]));
        result = null; return true;
    }
    return base.TryInvokeMember(binder, args, out result);
}

適用場景與替代方案對比

方案優點缺點適用場景
DynamicXMLNode語法極簡、動態構建、無需定義類別無編譯期檢查、效能稍低、命名空間處理繁瑣測試資料構建、動態配置、原型開發
XDocument/LINQ to XML強型別、標準 API、效能高語法冗長生產環境 XML 處理
XmlSerializerPOCO 雙向映射、Schema 驗證需預定義類別、嚴格對應業務實體持久化、API 契約
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved