I recently had an ObservableCollection which did not currently contain the item I was interested in, but it would sometime in the near future. The questions is how best to get notified that the item is available so I could access its full property set? I only had the item’s ID and the collection’s CollectionChanged event to work with.
My first instinct was to just go with simple lambda expression subscribed to the CollectionChanged event in order to filter for the item’s appearance in the collection
The problem with this pattern is that you can’t unsubscribe from the event notification. Every time something is added (even after we have found our item) this logic will execute wasting CPU processing power. In addition, this also has the potential to create memory issues/leaks because objects may remain alive longer since the subscription will exist as long as the collection does. If the subscription has side effects (there are none in this simple case) then they are essentially permanent.
We can go with a basic delegate structure which allows us to unsubscribe but the logic gets a little more complicated and we have to spread it out into multiple sections.
But now we’ve lost the encapsulation the lambda provided us so its harder to read. We also have to be careful because the code isn’t reentrant due to the state variables.
Now lets look at the Reactive Extensions solution
The Take(1) ensures we only have a single result. This causes the observable to complete after that one result. When observables complete, the subscription to the event is unsubscribed. Plus we don’t have any state to manage ourselves and the code is short and concise. It is readable and directly expresses our intended purpose to the reader.
This is an excellent pattern for any type of .NET event where you expect to receive a specific number of notifications instead of a permanent stream of notifications and you don’t want to worry about managing state or cleaning up your delegates.
Implementation detail notes
Rx is a great library that brings composability to events, I like it.
That said for simple use cases like your's I'd rather stick with a variation on your 1st try:
public void WaitForMovie_UsingLambda(int ID, ObservableCollection<MovieData> collection)
{
var handler = new NotifyCollectionChangedEventHAndler((sender, e) =>
if (e.Action != NotifyCollectionChangedAction.Add) return;
var movie = ((MovieData)e.NewItems[0]);
if (movie.ID != movieSearchID) return;
collection.CollectionChanged -= handler;
DoSomething(movie);
};
collection.CollectionChanged += handler;
}
True, you could do it that way as well. For simple cases it becomes a matter of preference. I do like the expressiveness of the LINQ query where its clear that I just want to filter and grab the first item. But I think that as soon as we begin to add any more requirements the original way quickly becomes more complex and harder to read.
For example, there is one slight difference between the two. The Rx version guarantees you wont have more than one call to the DoSomething() code. In your version there is a race condition if a qualifying event happens again before you unhook the handler. If we were concerned with this happening, we could modify the original again but that means adding explicit state and locks to manage concurrency which increases complexity of the code
thanks for the feedback!