Skip to main content
 首页 » 编程设计

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

2024年01月22日25bonelee

我想创建一个自定义可观察集合(可以在 XAML 中绑定(bind)),但我想跟踪需要覆盖可观察集合方法的其他信息。 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 
} 

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

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

    myCustomObj.AddToCollection( myCustomCollectionInherited ); 
... 
void CustomObj.AddToCollection( Collection c ) 
{    
    c.Add(this); 
} 

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


基于这些问题,我开始认为合适的解决方案是创建一个包含 ObservableCollection 的类。但请记住,我需要它像可观察集合一样绑定(bind)到 XAML,因此我必须实现 ObservableCollection 实现的所有相关接口(interface),以便它可以以相同的方式绑定(bind)到 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; 
    } 
} 

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

Type methodINeedToImplement(Params) 
{ 
     return _base.methodINeedToImplement(Params); 
} 

或

Type methodINeedToImplement(Params) 
{ 
     throw new NotImplementedException(); 
} 

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

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

请您参考如下方法:

ObservableCollection 被设计成一个基类,你只是在寻找错误的方法,而不是像 Add、Remove、Clear 等公共(public)方法。你应该重写 protected 虚拟方法,如 InsertItem、MoveItem 等。检查 documentation获取可覆盖内容的完整列表。