C#Custom Observable Collection - 我应该使用Composition还是继承?

时间:2011-10-05 18:36:16

标签: c# oop inheritance .net-3.5

我想创建一个自定义的可观察集合(可以绑定到XAML中),但我想跟踪需要覆盖可观察集合方法的其他信息。 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接口等,有许多我不想继承的方法。即,修改集合的方法,例如Clear(),ClearItems(),Insert(),InsertItem()和其他13个会导致我的集合的类型计数不同步的方法。这似乎是作文的论据。

第二个问题是 upcasting - 程序员可能会意外地使用我的集合来绕过我的自定义实现,这样它就会被上传到继承类型。例如:

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

这是一个非常人为的例子,但在这种情况下,将使用继承的“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;
    }
}

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

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

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

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

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

5 个答案:

答案 0 :(得分:4)

ObservableCollection被设计为基类,你只是看错了方法,而不是像Add,Remove,Clear等公共方法。你应该覆盖像InsertItem,MoveItem等受保护的虚拟方法。检查{{3有关可覆盖的东西的完整列表。

答案 1 :(得分:1)

如果你不能覆盖ObservableCollection中的方法,那么你应该使用CollectionBase并改为实现改变INotifyCollection。

虽然我认为您对问题的处理方法可能不合适,因为您希望提供更简单的集合视图,我认为您应该专注于这样做。创建一个ISimpleCollectionView,它只提供您要查找的接口,并编写一个接受ICollection并实现ISimpleCollectionView接口的适配器。

答案 2 :(得分:1)

您可以隐藏基类中的方法,以下是MSDN中的示例:

class Base
{
   public void F() {}
}
class Derived: Base
{
   public void F() {}      // Warning, hiding an inherited name
}

由于你没有给出“暗影”方法任何暗示,它只是删除它。然而,向上倾斜......是的,你被搞砸了。你继承了可观察的集合,它可以被铸成一个......

杰森

答案 3 :(得分:1)

假设您扩展ObservableCollection的唯一原因是保留所包含类型的计数,您可以通过直接从ObservableCollection继承并覆盖OnCollectionChanged (虚拟)来轻松完成此任务:

protected virtual void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)

这样,您不必担心如何在集合中添加/删除它,只需在之前增加/减少计数,即可调用base.OnCollectionChanged(e)方法

顺便说一下,为什么不使用链接到对象,而不是经历所有这些麻烦,为什么不使用链接到对象:myCollection.Count(p =&gt; p是MyType); ???

答案 4 :(得分:1)

我认为坚持继承是可以的(虽然你可以使用适配器)但是我会简单地覆盖(或处理如果使用适配器)OnCollectionChanged

public class CustomCollectionInherited : ObservableCollection<object>
{
    private Dictionary<Type, UInt32> _count = new Dictionary<Type, uint>();

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

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                e.NewItems.Cast<Object>().ToList().ForEach(OnAdded);
                break;
            case NotifyCollectionChangedAction.Remove:
                e.OldItems.Cast<Object>().ToList().ForEach(OnRemoved);
                break;
            case NotifyCollectionChangedAction.Replace:
                // TODO: Handle this case
                break;
            case NotifyCollectionChangedAction.Reset:
                _count.Clear();
                this.ToList().ForEach(OnAdded);
                break;
        }
        base.OnCollectionChanged(e);
    }

    private void OnAdded(Object o)
    {
        _count[o.GetType()] += 1;
    }

    private void OnRemoved(Object o)
    {
        _count[o.GetType()] -= 1;
    }
}