繁体   English   中英

C#自定义可观察集合-我应该使用合成还是继承?

[英]C# Custom Observable Collection - Should I use Composition or Inheritance?

我想创建一个自定义的可观察集合(可以在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种方法,这些方法会导致我的集合的类型计数不同步。 这似乎是构成的理由。

第二个问题是转换 -程序员可能会不小心使用我的集合来绕过我的自定义实现,从而将其转换为继承的类型。 例如:

    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方法来将其搞砸。

ObservableCollection设计为基类,您只是在查看错误的方法,而不是诸如Add,Remove,Clear等的公共方法。您应该覆盖受保护的虚拟方法(如InsertItem,MoveItem等)。请查看文档以获取完整的清单可替代的东西。

如果无法覆盖ObservableCollection中的方法,则应使用CollectionBase并实现已更改的INotifyCollection。

尽管我认为您要解决的问题的方法可能不合时宜,因为您想提供一个更简单的集合视图,但我认为您应该将精力集中在这样做上。 创建一个仅提供您要查找的接口的ISimpleCollectionView,并编写一个将ICollection引入并实现ISimpleCollectionView接口的适配器。

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

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

由于您没有给“阴影”方法带来任何隐含,因此只需删除它即可。 但是,up然……是的,你被搞砸了。 您从可观察的集合继承,可以将其转换为一个...

杰森

假设扩展ObservableCollection的唯一原因是保留所包含类型的数量,则可以通过直接从ObservableCollection继承并覆盖virtual的OnCollectionChanged来轻松实现此目的

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

这样,您不必担心如何将其添加到集合中或从集合中删除,只需调用base.OnCollectionChanged(e)方法之前递增/递减计数即可。

顺便说一句,为什么不使用链接到对象,而不是通过所有这些麻烦来获取类型的计数:myCollection.Count(p => p is MyType); ???

我认为坚持继承是可以的(尽管您可以使用适配器),但我可以使用OnCollectionChanged来替代“ new”功能,而不是简单地重写(或使用适配器处理)。

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;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM