How does ObservableCollection<T>.Add work? - c#

I was trying to implement a specialized collection that works like ObservableCollection to encapsulate some more mechanisms in it, to do that i also let my collection inherit from Collection and i also implement the same interfaces.
I just do not get though how one actually implements the whole collection-changed-logic, for example Collection<T>.Add is not being overridden (it is not even marked as virtual), so how does the ObservableCollection fire the CollectionChanged event if items were added using that method?

To answer your specific question, Collection<T>.Add calls the InsertItem virtual method (after checking that the collection is not read-only). ObservableCollection<T> indeed overrides this method to do the insert and raise the relevant change notifications.

It does so by calling InsertItem which is overridden and can be seen upon decompilation
protected override void InsertItem(int index, T item)
{
this.CheckReentrancy();
base.InsertItem(index, item);
this.OnPropertyChanged("Count");
this.OnPropertyChanged("Item[]");
this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
}

Remember, the key is not in overriding the base Collection methods, it's in the fact that you will be implementing the ICollection interface. And frankly, rather than inheriting from a Collection class, I would suggest instead creating an adapter class that takes a ICollection in the constructor and your methods will just delegate to the inner collection and raise the appropriate events.

Related

How to override or hide add in an ObservableCollection?

I want to override Add(MyType t) for a class derived from ObservableCollection<MyType>. However I cannot override Add. Why?
I therefore added AddIem(MyType t)and use that function instead, which works fine. But I want to prevent someone erroneously using Add so I implemented Add (throwing an exception). But that doesn't hide the Add method of the ObservableCollection. Any idea why and how I could achieve my goal?
Keep your ObservableCollection private, and expose the items with a public ReadOnlyObservableCollection which reflects the items in the private collection.
If you need to expose a specialized AddItem method to other classes, you could make it a member of your viewmodel class, or you could subclass ReadOnlyObservableCollection and put it there. Call it MostlyReadOnlyObservableCollection.

Why doesn't INotifyCollectionChanged extend IList?

I've ran into the situation a couple of times where I want to observe a collection through the INotifyCollectionChanged interface, but also want to be able to access any of the collection's elements. The INotifyCollectionChanged interface doesn't provide any way to access the elements, except for those that are involved in the change event (which (usually) are contained in the NotifyCollectionChangedEventArgs).
Now here's my thinking:
We know that whatever implements INotifyCollectionChanged is a collection (d'uh).
Since the NotifyPropertyChangedEventArgs contains indices indication the location of the change, we know that the elements can be accessed by index.
A collection that can be accessed by index is a list, so it seems to be that it would make sense to require that any INotifyCollectionChanged implementor also implements IList. This could easily be done by letting INotifyCollectionChanged extend IList.
Does anyone know why this is not the case?
I think you need to look up the SOLID software design principles, specifically the Liskov Substitution Principle.
You asked why the INotifyCollectionChanged interface does not also extend the IList interface. Let me answer it with a counter question using the Liskov Subsitution Principle:
Can I say an INotifyCollectionChanged is an IList?
No I don't think so, for the following reasons:
INotifyCollectionChanged conveys the meaning that classes implementing this interface need to notify their users if its underlying collection was changed, whether that underlying collection is an IList or ICollection, or even IEnumerable, we do not know. It's different concept of an IList interface, which is simply an ICollection with an exposed indexer
You mentioned NotifyPropertyChangedEventArgs (which I believe you meant NotifyCollectionChangedEventArgs instead) exposes properties of the indices indicating at what position the collection is changed. However this does not mean these properties necessarily expose the items through the indexers of IList. It can be an arbitrary number, a magic constant, whatever. It is up to the implementing class to decide how to expose the indices.
To demonstrate this, please take a look at my custom class that implements INotifyCollectionChanged:
public class MyCustomCollection : INotifyCollectionChanged
{
// This is what I meant by the "underlying collection", can be replaced with
// ICollection<int> and it will still work, or even IEnumerable<int> but with some
// code change to store the elements in an array
private readonly IList<int> _ints;
public MyCustomCollection()
{
_ints = new List<int>();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
public void AddInt(int i)
{
_ints.Add(i);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Move,
(IList)_ints,
_ints.Count,
_ints.Count - 1));
}
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
var handler = CollectionChanged;
if (handler != null)
{
handler(this, e);
}
}
}
Hope this answer your question.
I don’t know why, but I see reasons for that decision (without any knowledge of the decision – it’s just my opinion):
Implementing INotifyCollectionChanged this way follows the principle of role-interfaces (the change notification is described rather than the object) Please see #rexcfnghk post for that!
Only the changed elements in the EventArgs are represented as an IList<T>, that does not mean that the collection itself needs to be a list
To conclude, they made a decision to make the default collection with change notification a Collection<T> using an interface that allows that was required.

Why BindingList<T> RemoveItem method is protected

Tell me please, why BindingList RemoveItem method is protected? What is the purpose of it? I know I can just inherit from it but the question is why one have made this protected
This is protected because BindingList<T> is based on Collection<T>, and RemoveItem is intended for implementations of Collection<T> to provide the proper, collection-specific implementation.
To use this directly, you can use the public RemoveAt method. This calls RemoveItem internally, but is the public-facing API for removing an item by index.

C#: ObservableCollection - why no generic "CollectionChanged" event?

This is not so much a question, and more something that struck me as odd: the ObservableCollection class is generic, but the CollectionChanged event that it raises when changed to the collection occur is not. That means within the event handler, you have to cast all the objects in the NewItems/OldItems collections you get from the event args object to the proper item type yourself.
But shouldn't it have been simply possible to make that event generic? Something like
public virtual event NotifyCollectionChangedEventHandler<T> CollectionChanged;
with
public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs<T> e);
and then
public class NotifyCollectionChangedEventArgs<T> {
// ...
public IList<T> NewItems { get; }
public IList<T> OldItems { get; }
After all, the type parameter T is already determined by the declaration of the ObservableCollection in question, which means you should never be able to add any objects that are not of type T (or compatible to it) to the collection anyway. So all of this should be perfectly type safe, and save us the trouble of having to cast the objects inside the event handler into whatever type we know they should have anyway.
Is there a reason for not doing it this way that I am missing? I.e. is this a conscious design desicion, or just an oversight?
The INotifyCollectionChanged interface, along with INotifyPropertyChanged, were designed exclusively for use by the WPF framework. Since WPF is loosely typed, I'm guessing that generics weren't part of that design. I don't think INotifyCollectionChanged was designed to be consumed directly. Whereas, ObservableCollection<T> is designed to be consumed by C# code, which is strongly typed, therefore generics featured in the design.

Persistent collections and standard collection interfaces

I'm implementing a persistent collection - for the sake of argument, let's say it's a singly-linked list, of the style common in functional languages.
class MyList<T>
{
public T Head { get; }
public MyList<T> Tail { get; }
// other various stuff
// . . .
}
It seems natural to have this class implement ICollection<T>, since it can implement all the normal behavior one would expect of an ICollection<T>, at least in broad strokes. But there is a lot of mismatch between this class's behavior and ICollection<T>. For example, the signature of the Add() method
void Add(T item); // ICollection<T> version
assumes that the addition will be performed as a side-effect that mutates the collection. But this is a persistent data structure, so Add() should instead create a new list and return it.
MyList<T> Add(T item); // what we really want
It seems the best way to resolve this is to just create the version we want, and also generate a non-functional explicit implementation of the version defined in the interface.
void ICollection<T>.Add(T item) { throw new NotSupportedException(); }
public MyList<T> Add(T item) { return new MyList<T>(item, this); }
But I have a few concerns about that option:
Will this be confusing to users? I envision scenarios where someone is working with this class, and finds that calling Add() on it sometimes raises an exception, and sometimes runs but doesn't modify the list as would normally be expected for an ICollection, depending on the type information associated with the reference being used?
Following on (1), the implementation of ICollection<T>'s IsReadOnly should presumably return true. But that would seem to conflict with what is implied in other spots where Add() is being used with instances of the class.
Is (2) resolved in a non-confusing way by following the explicit implementation pattern again, with the new version returning false and the explicit implementation returning true? Or does this just make it even worse by falsely implying that MyList<T>'s Add() method is a mutator?
Or would it be better to forget trying to use the existing interface and just create a separate IPersistentCollection<T> interface that derives from IEnumerable<T> instead?
edit I changed the name of the class, and switched over to using ICollection. I wanted to focus on the object's behavior and how it relates to the interface. I just went with the cons list as a simple example. I appreciate the advice that if I were to implement a cons list I should try and come up with a less-confusing name and, should avoid implementing IList because that interface is intended for fast random access, but they are somewhat tangential issues.
What I intended to ask about is what others think about the tension between the semantics of read-only (or immutable) collections that are baked into the Framework, and persistent collections which implement equivalent behavior to what is described by the interface, only functionally rather than through mutating side effects.
Will implementing IList<T> be confusing?
Yes. Though there are situations in which an implementation of IList<T> throws -- say, when you are attempting to resize the list but its implementation is an array -- I would find it quite confusing to have an IList<T> that could be mutated in no way and did not have fast random access.
Should I implement a new IPersistentList<T>?
That depends on whether anyone will use it. Are consumers of your class likely to have a half-dozen different implementations of IPL<T> to choose from? I see no point in making an interface that is implemented by only one class; just use the class.
WPF's ItemsControl can get better performance if its ItemsSource is an IList<T> instead of an IEnumerable<T>.
But your persistent linked list will not have fast random access anyway.
It would make more sense to me to make a new IPersistentList<T> (or IImmutableList<T> since "persistent" sounds to me like the data is saved off somewhere.) interface since, really, it's different behavior than what is expected of an IList<T>. Classes that implement IList<T> should be mutable IMHO.
Oh, and of course, I'd avoid using the class name List<T> since it's already part of the framework.

Categories

Resources