Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
2.0k views
in Technique[技术] by (71.8m points)

oop - C# Custom Observable Collection - Should I use Composition or Inheritance?

I want to create a custom observable collection (can be bound to in XAML), but I want to track additional information which requires overriding observable collection methods. ObservableCollection methods are not virtual though, which means the only way to 'override' them is actually just to hide them using the 'new' keyword. Here is a simple example of what I mean:

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

I have two problems with this. The first is that while there are many methods I want to inherit from an ObservableCollection, such as the enumerators, the INotifyCollectionChanged interface, etc., there are many methods which I do not want to inherit. Namely, methods that modify the collection, such as Clear(), ClearItems(), Insert(), InsertItem(), and 13 others which would cause my collection's type count to get out of sync. This seems to be an argument for composition.

The second problem is upcasting - a programmer might accidentally get around my custom implementations by using my collection in such a way that it gets upcast to the inherited type. For example:

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

It's a pretty contrived example, but in such a case, the inherited 'Add' method would be used, and my collection's type count would get out of sync again. There doesn't seem to be any way around this unless my collection monitors the base.CollectionChanged event and rebuilds the count from scratch every time, which completely defeats the purpose of maintaining a count in O(1) time.


Based on those issues, I've started to think that the appropriate solution is to create a class which contains an ObservableCollection. But remember, I need this to bind to XAML like an observable collection, so I must implement all the relevant interfaces an ObservableCollection implements so that it can be bound to the UI in the same way. An example is below:

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

Of course, the above on its own doesn't compile because IList implements ICollection, IEnumerable, IEnumerable, all of which have methods I need to implement, and so on until I end up having 20 or so extra methods and hundreds of lines of code all of which say

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

or

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

The main reason for inheritance is so that a programmer does not need to do all this work for the 95% of methods and events they aren't changing.

So what do I do? I absolutely cannot convince my boss that the best way to secure this custom collection is to use encapsulation and explicitly implement 20 new methods. At the same time, we're already running into bugs where other people using this custom collection are screwing it up by using base ObservableCollection methods that we don't support, but can't hide via inheritance.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

ObservableCollection is designed to be a base class, you are just looking at wrong methods, instead of public ones like Add, Remove, Clear, etc. you should override protected virtual ones like InsertItem, MoveItem, etc. Check documentation for a full list of overridable stuff.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...