C#類繼承自泛型集合

VinciYan發表於2024-08-09

繼承自泛型字典的例子

這段程式碼定義了一個多層巢狀的字典結構,旨在組織和儲存複雜的層級資料

using System;
using System.Threading.Tasks;
class Contract : Dictionary<string, Dictionary<string, Dictionary<string, string>>>
{
    private readonly string type = "autodesk.data:exchange.contract.dynamo-1.0.0";

    public Contract()
    {
        var contractContent = new Dictionary<string, Dictionary<string, string>>
            {
                { "contract", new Dictionary<string, string>() }
            };

        Add(type, contractContent);
    }
}
class Program
{
    static void Main()
    {
        // Create an instance of Contract
        var contract = new Contract();

        // Access the outer dictionary using the predefined type key
        var typeKey = "autodesk.data:exchange.contract.dynamo-1.0.0";

        // Check if the key exists and add data
        if (contract.TryGetValue(typeKey, out var contractContent))
        {
            // Access the inner dictionary with the key "contract"
            if (contractContent.TryGetValue("contract", out var innerDictionary))
            {
                // Add key-value pairs to the innermost dictionary
                innerDictionary["key1"] = "value1";
                innerDictionary["key2"] = "value2";

                // Retrieve and display values
                Console.WriteLine(innerDictionary["key1"]); // Outputs: value1
                Console.WriteLine(innerDictionary["key2"]); // Outputs: value2
            }
        }
    }
}
value1
value2

再看一個Dynamo專案例項,這段程式碼的目的是建立一個巢狀字典結構,用於儲存有關二進位制引用元件的屬性資料。使用介面IPropertySet作為多型機制的基礎。透過巢狀字典結構實現多層次資料的組織和訪問。提供靈活的屬性管理,適合複雜物件的屬性儲存場景

using System;
using System.Threading.Tasks;
class BinaryReferenceComponent : Dictionary<string, Dictionary<string, IPropertySet>>
{
    private string objectId = "autodesk.data:binary.reference.component-1.0.0";
    public string ObjectId { get { return objectId; } }
    public BinaryReferenceComponent(string binaryId)
    {
        var propertyDictionary = new Dictionary<string, IPropertySet>();
        propertyDictionary.Add("String", new StringPropertySet(binaryId));
        propertyDictionary.Add("Uint32", new IntPropertySet());

        this.Add("binary_reference", propertyDictionary);
    }
}

class StringPropertySet : IPropertySet
{
    public string Id { get; set; }
    public string Revision { get; set; }

    public StringPropertySet(string binaryId, string revision = "v0")
    {
        Id = binaryId;
        Revision = revision;
    }
}

class IntPropertySet : IPropertySet
{
    public int End { get; set; }
    public int Start { get; set; }

    public IntPropertySet(int start = 0, int end = 8710)
    {
        End = end;
        Start = start;
    }
}

interface IPropertySet { }
class Program
{
    static void Main()
    {
        // Create an instance of BinaryReferenceComponent with a binaryId
        var binaryReference = new BinaryReferenceComponent("exampleBinaryId");

        // Access ObjectId
        Console.WriteLine($"ObjectId: {binaryReference.ObjectId}");

        // Access properties of the component
        if (binaryReference.TryGetValue("binary_reference", out var propertySet))
        {
            if (propertySet.TryGetValue("String", out var stringProperty))
            {
                var stringProp = stringProperty as StringPropertySet;
                Console.WriteLine($"StringProperty Id: {stringProp.Id}, Revision: {stringProp.Revision}");
            }

            if (propertySet.TryGetValue("Uint32", out var intProperty))
            {
                var intProp = intProperty as IntPropertySet;
                Console.WriteLine($"IntProperty Start: {intProp.Start}, End: {intProp.End}");
            }
        }
    }
}
ObjectId: autodesk.data:binary.reference.component-1.0.0
StringProperty Id: exampleBinaryId, Revision: v0
IntProperty Start: 0, End: 8710

繼承多種集合型別

在C#中,除了泛型字典外,你還可以繼承其他集合型別,例如:

  • List:可以建立自定義列表類,但不常見,建議使用組合
  • HashSet:用於無重複元素集合,但繼承並不常見
  • Queue和Stack:分別用於佇列和棧的實現
  • Collection和ReadOnlyCollection:更適合繼承,提供了更好的方法定製化能力

示例:繼承 Collection

程式碼說明

  1. CustomCollection<T>:

    • 繼承自Collection<T>
    • 重寫了InsertItem方法,新增插入前後的自定義行為
    • 定義了一個ItemAdded事件,當新專案新增時觸發
  2. Main函式:

    • 建立CustomCollection<string>的例項
    • 訂閱ItemAdded事件,輸出新增的項資訊
    • 新增幾個專案並顯示集合中的所有專案

關鍵點

  • 事件處理: 使用事件機制通知項的新增
  • 自定義行為: 透過重寫方法實現特定邏輯
  • 靈活性: CustomCollection<T>可以用於任何型別的集合
using System;
using System.Collections.ObjectModel;

class CustomCollection<T> : Collection<T>
{
    // Event triggered when an item is added
    public event Action<T> ItemAdded;

    // Custom implementation of InsertItem
    protected override void InsertItem(int index, T item)
    {
        // Add custom behavior before inserting
        Console.WriteLine($"Inserting item at index {index}: {item}");
        base.InsertItem(index, item);
        // Trigger the event after inserting
        ItemAdded?.Invoke(item);
    }

    // Method to display all items
    public void DisplayItems()
    {
        Console.WriteLine("Current items in collection:");
        foreach (var item in this)
        {
            Console.WriteLine(item);
        }
    }
}

class Program
{
    static void Main()
    {
        // Create an instance of CustomCollection
        var collection = new CustomCollection<string>();

        // Subscribe to the ItemAdded event
        collection.ItemAdded += item => Console.WriteLine($"Item added: {item}");

        // Add items to the collection
        collection.Add("Item1");
        collection.Add("Item2");
        collection.Add("Item3");

        // Display all items in the collection
        collection.DisplayItems();
    }
}

執行結果:

Inserting item at index 0: Item1
Item added: Item1
Inserting item at index 1: Item2
Item added: Item2
Inserting item at index 2: Item3
Item added: Item3
Current items in collection:
Item1
Item2
Item3

注意

在C#中,類繼承自泛型字典並不常見。以下是一些原因和建議:

原因

  • 違背封裝原則:直接繼承集合類可能導致外部程式碼直接操作集合內部結構,違背封裝原則
  • 集合行為的複雜性:集合類提供的行為可能不完全適合自定義類的需求,可能需要重寫大量方法。繼承集合類可能引入維護複雜性
    組合優於繼承:常用的設計模式是組合,即在類內部包含一個集合,而不是繼承它

建議

  • 使用組合:在類中定義一個集合欄位,並透過方法或屬性操作它。這可以更好地控制訪問和行為
    擴充套件方法:如果需要對集合進行特定操作,可以使用擴充套件方法來增強其功能,而不是繼承

將前面的一個繼承的例子改為使用組合,執行結果不變

using System;
using System.Collections.Generic;

interface IPropertySet { }

class StringPropertySet : IPropertySet
{
    public string Id { get; set; }
    public string Revision { get; set; }

    public StringPropertySet(string binaryId, string revision = "v0")
    {
        Id = binaryId;
        Revision = revision;
    }
}

class IntPropertySet : IPropertySet
{
    public int End { get; set; }
    public int Start { get; set; }

    public IntPropertySet(int start = 0, int end = 8710)
    {
        End = end;
        Start = start;
    }
}

class BinaryReferenceComponent
{
    private Dictionary<string, Dictionary<string, IPropertySet>> properties = new();

    public string ObjectId { get; } = "autodesk.data:binary.reference.component-1.0.0";

    public BinaryReferenceComponent(string binaryId)
    {
        var propertyDictionary = new Dictionary<string, IPropertySet>
        {
            { "String", new StringPropertySet(binaryId) },
            { "Uint32", new IntPropertySet() }
        };

        properties.Add("binary_reference", propertyDictionary);
    }

    public Dictionary<string, IPropertySet> GetProperties(string key)
    {
        properties.TryGetValue(key, out var result);
        return result;
    }
}

class Program
{
    static void Main()
    {
        // Create an instance of BinaryReferenceComponent
        var binaryReference = new BinaryReferenceComponent("exampleBinaryId");

        // Access ObjectId
        Console.WriteLine($"ObjectId: {binaryReference.ObjectId}");

        // Retrieve properties using GetProperties method
        var properties = binaryReference.GetProperties("binary_reference");

        if (properties != null)
        {
            if (properties.TryGetValue("String", out var stringProperty))
            {
                var stringProp = stringProperty as StringPropertySet;
                Console.WriteLine($"StringProperty Id: {stringProp.Id}, Revision: {stringProp.Revision}");
            }

            if (properties.TryGetValue("Uint32", out var intProperty))
            {
                var intProp = intProperty as IntPropertySet;
                Console.WriteLine($"IntProperty Start: {intProp.Start}, End: {intProp.End}");
            }
        }
        else
        {
            Console.WriteLine("No properties found for the given key.");
        }
    }
}

參考

  • https://github.com/DynamoDS/Dynamo.git

相關文章