C# 自定义 Observable 集合 - 我应该使用组合还是继承?

Ala*_*ain 3 c# oop inheritance .net-3.5

我想创建一个自定义 observable 集合(可以在 XAML 中绑定),但我想跟踪需要覆盖 observable 集合方法的其他信息。ObservableCollection 方法不是虚拟的,这意味着“覆盖”它们的唯一方法实际上只是使用“new”关键字隐藏它们。这是我的意思的一个简单示例:

//Tracks how many objects of each type are in this collection
class CustomCollectionInherited:ObservableCollection<Object>
{
    private Dictionary<Type, UInt32> _count = new Dictionary<Type,uint>();

    public UInt32 getTypeCount(Type T)
    {
        return _count[T];
    }

    #region Base Method 'Overrides'

    new public void Add(Object item)
    {
        base.Add(item);
        if (!_count.ContainsKey(item.GetType())) {
            _count[item.GetType()] = 1;
        } else {
            _count[item.GetType()] += 1;
        }
    }

    new public bool Remove(Object item)
    {
        if (base.Remove(item))
        {
            _count[item.GetType()] -= 1;
            return true;
        }
        return false;
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)

我有两个问题。首先,虽然我想从 ObservableCollection 继承很多方法,例如枚举器、INotifyCollectionChanged 接口等,但有很多方法我不想继承。即,修改集合的方法,例如 Clear()、ClearItems()、Insert()、InsertItem() 和其他 13 个会导致我的集合的类型计数不同步的方法。这似乎是组成的论据。

第二个问题是向上转换- 程序员可能会意外绕过我的自定义实现,通过使用我的集合的方式将其向上转换为继承的类型。例如:

    myCustomObj.AddToCollection( myCustomCollectionInherited );
...
void CustomObj.AddToCollection( Collection c )
{   
    c.Add(this);
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常人为的示例,但在这种情况下,将使用继承的“Add”方法,并且我的集合的类型计数将再次不同步。除非我的集合监视 base.CollectionChanged 事件并每次从头开始重建计数,否则似乎没有任何解决方法,这完全违背了在 O(1) 时间内维护计数的目的。


基于这些问题,我开始认为适当的解决方案是创建一个包含ObservableCollection的类。但请记住,我需要它像可观察集合一样绑定到 XAML,因此我必须实现 ObservableCollection 实现的所有相关接口,以便它可以以相同的方式绑定到 UI。一个例子如下:

//Tracks how many objects of each type are in this collection
class CustomCollectionEncapsulated : IList<object>, INotifyCollectionChanged
{
    private ObservableCollection<Object> _base = new ObservableCollection<object>();
    private Dictionary<Type, UInt32> _count = new Dictionary<Type, uint>();

    public UInt32 getTypeCount(Type T)
    {
        return _count[T];
    }

    public void Add(object item)
    {
        _base.Add(item);
        if (!_count.ContainsKey(item.GetType())) {
            _count[item.GetType()] = 1;
        } else {
            _count[item.GetType()] += 1;
        }
    }

    public bool Remove(object item)
    {
        if (_base.Remove(item))
        {
            _count[item.GetType()] -= 1;
            return true;
        }
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,上面的内容本身并不能编译,因为 IList 实现了 ICollection、IEnumerable、IEnumerable,所有这些都有我需要实现的方法,依此类推,直到我最终拥有 20 个左右的额外方法和数百行代码所有这些都说

Type methodINeedToImplement(Params)
{
     return _base.methodINeedToImplement(Params);
}
Run Code Online (Sandbox Code Playgroud)

或者

Type methodINeedToImplement(Params)
{
     throw new NotImplementedException();
}
Run Code Online (Sandbox Code Playgroud)

继承的主要原因是程序员不需要为 95% 没有改变的方法和事件做所有这些工作。

那我该怎么办?我绝对无法说服我的老板,保护这个自定义集合的最佳方法是使用封装并明确实现 20 种新方法。与此同时,我们已经遇到了其他人使用这个自定义集合的错误,他们通过使用我们不支持但无法通过继承隐藏的基本 ObservableCollection 方法来搞砸它。

Kon*_*hin 6

ObservableCollection 被设计成一个基类,你只是在看错误的方法,而不是像 Add、Remove、Clear 等公共方法。你应该覆盖像 InsertItem、MoveItem 等受保护的虚拟方法。检查文档以获取完整的列表可覆盖的东西。

  • @Alain:您是否阅读了这些方法的文档?它们不是特例,而是 Collection&lt;T&gt; 所基于的核心。所有公共方法,如 Add、Remove、Insert 等,最终都会调用这些受保护的虚拟方法。毕竟ObservableCollection类就是这样实现的,它没有Add方法,公共的非虚A​​dd方法是Collection类定义的,ObservableCollection只是继承并覆盖了InsertItem。你为什么不看看文档? (7认同)