【问题标题】:Correct way to implement a custom collection that rely on object subproperty?实现依赖对象子属性的自定义集合的正确方法?
【发布时间】:2012-07-05 08:26:09
【问题描述】:

假设我有这样的课程:

public class MyClass {

    public int Id { get; set;}
    public string Name { get; set; }

}

请注意,在我的例子中,这个类是失控的(来自第三方库)并且没有实现相等比较器。

现在我想要一个这些项目的集合,允许我根据它们的索引删除项目。

我希望能够做到:

void Foo()
{
    var collection = new MyClassCollection();

    var a = new MyClass { Id = 1, Name = "A" };
    var b = new MyClass { Id = 2, Name = "B" };

    collection.Add(a);
    collection.Add(b);

    // Other instance, that can come from external code
    var otherA = new MyClass { Id = 1, Name = "A" };
    var otherB = new MyClass { Id = 2, Name = "B" };

    collection.Remove(otherA);

    Console.WriteLine(collection.Count); // should output 1

    Console.WriteLine(collection.Contains(otherB)); // should be true

    collection.Add(otherB); // 

    Console.WriteLine(collection.Count); // should still output 1
}

实际上,在集合中搜索时只应考虑 ID。此代码是依赖序列化 DTO 的项目的一部分,因此不能依赖实例。

我应该如何实现 MyClassCollection?

我想到了两种可能性,但对我来说都不是优雅的:

  1. List<MyClass> 继承并添加新方法。这将使 .Add 和 .Remove 保持原样,并可能导致消费者代码出现问题
  2. 创建一个实现ICollection<MyClass> 的类。创建一个内部Dictionary<int, MyClass> 来存储值,并使用字典内置功能在添加或删除之前检查键。通过包装内部字典值来实现接口的所有方法

感谢您对我的案件的看法。

PS:我希望列表中的元素很少(

编辑:我使用的是 .Net 3.5 SP1。

Edit2:根据 Jon 的建议,这是我的实现:

public class MyClassCollection : HashSet<MyClass>
{
    private class MyClassIDComparer : IEqualityComparer<MyClass>{

        public bool Equals(MyClass x, MyClass y)
        {
            if (x == null && y == null) return true;
            else if (x == null || y == null) return false;
            else return x.ID == y.ID;
        }

        public int GetHashCode(MyClass obj)
        {
            return obj.ID.GetHashCode();
        }
    }
    public MyClassCollection() : base(new MyClassIDComparer())
    {

    }
}

【问题讨论】:

    标签: c# collections


    【解决方案1】:

    HashSet&lt;MyClass&gt; 非常适合您的用例,因为它可以模拟您的意图,并且可以使用自定义 IEqualityComparer&lt;MyClass&gt; 进行配置(因此,MyClass 不实现 IEquatable&lt;MyClass&gt; 并不重要)。

    不幸的是HashSet 没有实现IList,所以它不能直接进行基于索引的访问——但你真的需要吗?也许可以通过重新考虑业务逻辑的某些部分来放宽此要求?

    【讨论】:

    • +1 优雅。如果您想避免人们使用错误类型的集合,只需从 HashSet&lt;MyClass&gt; 继承并调用基本构造函数来强制包含比较器。
    • 我想我可以离开没有索引器。我唯一需要的是 Add 和 Remove 方法...
    • 嗯,我遇到了这种方法的问题。在我实现这个方法之前,我使用了一个 List。插入顺序被保留。使用哈希集,我相信实际的顺序将是哈希顺序,对吧?在这种情况下,如何在保留顺序的同时结合 hashset 的优势?
    • @SteveB:视情况而定。它需要一个辅助容器或将您的对象包装在另一个保留排序信息的结构中(例如Tuple&lt;MyClass, int&gt;,其中数字是递增序列)。无论如何,它最终会远远超过当前的 10 行代码......
    • 在 net 3.5 中,元组不存在。无论如何,我会做这样的 KeyValuePair 可能会帮助我。谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-07
    • 1970-01-01
    相关资源
    最近更新 更多