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

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.

Related

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.

Should I raise INotifyPropertyChanged events if I don't have a corresponding property with that name on my class?

I have a class that implements INotifyPropertyChanged and also has its own concept of properties, a little bit like this:
class sealed MyClass : INotifyPropertyChanged
{
private Dictionary<string, object> _properties;
public object GetProperty(string name)
{
return _properties[name];
}
public object SomeProperty
{
get
{
return GetProperty("SomeProperty");
}
}
}
Note that some common properties also have C# accessors.
I want to raise an event to notify others that a property (whose value is accessed through the GetProperty method) has changed even if this property does not have a corresponding C# accessor.
Assuming that there is no risk of a clash (i.e. inadvertantly raising a property changed event for a C# property whose value has not changed - note that this class is sealed), is there any reason why I can't just use the INotifyPropertyChanged.PropertyChanged event to notify others, or should I add my own event for this purpose?
I would suggest that you add your own. Although there's no specific reason that the code would break, INotifyPropertyChanged has a specific intended usage, and one that is understood by a lot of potential consumers. I'd not personally write code that contradicts that, just in case.
As far as I know, there's no harm in raising PropertyChanged for a property that isn't there, but I don't think it's very useful either. INotifyPropertyChanged is mostly used by data binding systems (in WinForms, WPF, Silverlight...), and these system don't know how to access your custom "properties".
Now, what you could do is implement ICustomTypeDescriptor to provide access to your custom properties in a standard way. This works with Windows Forms and WPF, I'm not sure about other frameworks.
Of course, if your intention is not to use this for data binding, you can still use this event to notify consumers of your class that values have changed.
Technically, it depends on who has subscribed to PropertyChanged and what they do in response to the PropertyChanged event. If they don't try to read a .NET "property", you're fine.
But, that really introduces a coupling and the use of INPC is trying to decouple. INPC at the general case assumes PropertyChanged means a .NET property (of a given name) has changed and is expected to be the case for all places that use INPC. i.e. you implement INPC because you want to use your object in any place that accepts INPC. If your object can't do that, then it violates the implied contract of INPC. If you have a PropertyChanged handler that does something different than read a property and you're re-using INPC just because it's there then maybe writing you own interface is a good idea.

Events aren't fields - I don't get it

In C# in depth (an excellent book thus far), Skeet explains events aren't fields. I read this section many times and I don't understand why the distinction makes any difference.
I am one of those developers that confuse events and delegate instances. In my mind, they are the same. Aren't both just a form of indirection? We can multicast both. An event is setup as a field as shorthand...sure. But, we are adding or removing handlers. Stacking them up to be called when the event fires. Don't we do the same thing with delegates, stack them up and call invoke?
The other answers are basically correct, but here's another way to look at it:
I am one of those developers that confuse events and delegate instances. In my mind, they are the same.
An old saying about not seeing the forest for the trees comes to mind. The distinction that I make is that events are at a higher "semantic level" than a field of delegate instance. An event tells the consumer of the type "hi there, I am a type that likes to tell you when something happens". The type sources an event; that's part of its public contract.
How, as an implementation detail, that class chooses to keep track of who is interested in listening to that event, and what and when to tell the subscribers that the event is happening, is the business of the class. It happens to typically do so with a multicast delegate, but that's an implementation detail. It is such a common implementation detail that it is reasonable to confuse the two, but we really do have two different things: a public surface, and a private implementation detail.
Similarly, properties describe the semantics of an object: a customer has a name, so a Customer class has a Name property. You might say that "their name" is a property of a customer, but you would never say that "their name" is a field of a customer; that's an implementation detail of a particular class, not a fact about the business semantics. That a property is typically implemented as a field is a private detail of the class mechanics.
Properties aren't fields either, although they feel like them. They are actually a pair of methods (getter and setter) with special syntax.
Events are similarly a pair of methods (subscribe and unsubscribe) with special syntax.
In both cases, you usually have a private "backing field" inside your class, which holds the value manipulated by the getter/setter/subscribe/unsubscribe methods. And there's an auto-implemented syntax for both properties and events where the compiler generates the backing field and accessor methods for you.
The purpose is also the same: Properties provide restricted access to a field, where some validation logic is run before storing a new value. And an event provides restricted access to a delegate field, where consumers can only subscribe or unsubscribe, not read the list of subscribers, nor replace the whole list at once.
Let's consider the two ways to declare events.
Either you declare an event using an explicit add/remove method, or you declare an event without such methods.
In other words, you declare the event like this:
public event EventHandlerType EventName
{
add
{
// some code here
}
remove
{
// some code here
}
}
or you declare it like this:
public event EventHandlerType EventName;
The thing is, in some ways they're the same thing, and in other ways, they're completely different.
From the perspective of outside code, that is ... code outside of the class publishing the event, they're the exact same thing. To subscribe to an event, you call a method. To unsubscribe, you call a different method.
The difference is that in the second example code above, those methods will be provided by the compiler for you, however, that's still how it's going to be. To subscribe to the event, you call a method.
The syntax to do so, in C#, however, is the same, you do either:
objectInstance.EventName += ...;
or:
objectInstance.EventName -= ...;
So from the "outside perspective", the two ways are no different at all.
However, inside the class, there is a difference.
If you try to access the EventNameidentifier inside the class, you're actually referring to the field that backs the property, but only if you use the syntax that doesn't explicitly declare an add/remove method.
A typical pattern is like this:
public event EventHandlerType EventName;
protected void OnEventName()
{
var evt = EventName;
if (evt != null)
evt(this, EventArgs.Empty);
}
In this case, when you're referring to EventName, you're actually referring to the field that holds the delegate of type EventHandlerType.
However, if you've explicitly declared the add/remove methods, referring to the EventName identifier inside the class will be just like outside of the class, since the compiler cannot guarantee that it knows the field, or any other mechanism, in which you store the subscription.
An event is an accessor for a delegate. Just like a property is an accessor for a field. With the exact same utility, it prevents code from messing with the delegate object. Like a property has a get and set accessor, an event has the add and remove accessor.
It does behave somewhat different from a property, if you don't write the add and remove accessors yourself then the compiler auto-generates them. Including a private backing field that stores the delegate object. Similar to an automatic property.
You don't do this often but it is certainly not unusual. The .NET framework pretty commonly does so, for example the events of the Winforms controls are stored in an EventHandlerList and the add/remove accessors manipulate that list through its AddHandler() and RemoveHandler() methods. With the advantage that all the events (there are many) require only a single field in the class.
I can add to the former answers that delegates can be declared inside a namespace scope (outside a class) and events can be declared only inside a class.
This is because delegate is a class!
Another distinction is that , for events, the containing class is the only one that can fire it.
You can subscribe/unsubscribe to it via the containing class, but can't fire it (in contrast to delegates).
So maybe you can understand now why the convention is to wrap it inside a protected virtual OnSomething(object sender, EventArgs e). It is for the descendants to be able to override the implementation of the firing.

How does ObservableCollection<T>.Add work?

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.

ObservableCollection PropertyChanged event

I want to subclass ObservableCollection to add a property to it. Unfortunately, the PropertyChanged event is protected. Basically, I want to subclass it to have a SelectedItem that I can bind to for lists in my MVVM WPF app.
Here's the skeleton of my class:
public class SelectableList<T> : ObservableCollection<T>
{
public T SelectedItem {get;set;}
}
But I cannot do the following:
SelectableList<int> intList = new SelectableList<int>();
intList.PropertyChanged += new PropertyChangedEventHandler(intList_Changed);
because of access restrictions. This causes me to ask a deeper question. How is the UI notified of PropertyChanged events (e.g. Count property)? Note that I cannot do it in a code-behind.
My head is spinning, can someone please enlighten me?
SelectableList<int> intList = new SelectableList<int>();
((INotifyPropertyChanged)intList).PropertyChanged +=
new PropertyChangedEventHandler(intList_Changed);
ObservableCollection implements INotifyPropertyChanged explicitly, which means you have to cast the instance to the interface before you can access the interface's methods, properties and events. As to why this is done, I don't know. The Binding markup extension doesn't "know" ObservableCollections or any other type. It checks types to see if they implement or extend specific interfaces/base classes (INPC, INCC, DependencyObject, etc) and so doesn't care if the interface is implemented explicitly.
ObservableCollection (int .NET 3.5) appears to implement the PropertyChanged event in an interesting way.
protected event PropertyChangedEventHandler PropertyChanged;
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
This means that the protected PropertyChanged event is likely only meant to be used for internal implementation. The other INotifyPropertyChanged.PropertyChanged event is the one that actually fulfills the implementation of the INotifyPropertyChanged interface as an explicit interface. Strangely I do not see any place within the ObservableCollection where the INotifyPropertyChanged.PropertyChanged is actually raised. This may signal that this was a bug in .NET 3.5 although I haven't tested to confirm whether for example a property changed event is raised for Count when an item is added to a collection but that appears to be how it is supposed to work.
In the .NET 4.0 implementation it appears that the INotifyPropertyChanged.PropertyChanged event instead hooks to the same private delegate used by the protected PropertyChanged event which may have been a bug fix. It is also possible this is just due to differences in how auto event implementations are handled in .NET 4.0.
Correction: I have verified that the INotifyPropertyChanged.PropertyChanged event is raised by ObservableCollection so the assumptions I made above based on results from using Reflector to look at the ObservableCollection implementation must be inaccurate. My guess is that reflector is doing something strange bug I have no proof of that yet.
So to get your example to work you would need to write for this to work would look like the example below just as Will has demonstrated in his answer.
SelectableList<int> intList = new SelectableList<int>();
((INotifyPropertyChanged)intList).PropertyChanged +=
new PropertyChangedEventHandler(intList_Changed);
Interesting right? Using explicit interfaces is mainly used to avoid inevitable collisions in members required for a given interface but they can be used to in a sense hide the existence of a member.
If you would like to raise property change events for your own custom properties that you introduce in your subclass look into overriding and/or calling the protected OnPropertyChanged method that ObservableCollection also implements. This technique is a well adopted standard and allows subclasses to raise events or handle events without having access to the underlying event delegate. It is generally preferred to use this technique too by the way instead of having a subclass hook event handlers to it's own base classes events. For more examples look at how events in various controls are implemented in WinForms and WPF.
I tried to add a new property in
public class ResultCollection<T> : ObservableCollection<T>
{
Boolean _val;
public Boolean Val
{
get
{
return _val;
}
set
{
_val= value;
OnPropertyChanged(new PropertyChangedEventArgs("Val"));
}
}
}
I really didn't notice that PropertyChanged is defined as protected. Finally moved Val property to ViewModel.
The UI can and does get notified. This is a restriction JUST with ObservableCollection, which defines the PropertyChanged event as protected.
FWIW, I think you're better off leaving ObservableCollection alone and just adding another property to your VM.

Categories

Resources