Most of you are probably familiar with the ITypedList interface. This old post of mine shows a potential use case, and there are lots of other reasons to make use of this powerful way of influencing the mechanics of .NET data binding.
Implementing the interface is easy enough, but an interface implementation is really rather an inconvenient way of attaching a set of property descriptors to a collection – so it regularly happens that we have a collection instance at some point in our code, and we’d like to attach a certain set of property descriptors to it. The conventional way means that we have to create a derived collection type and implement ITypedList, modify existing code to make sure this derived type is returned, and finally we can benefit from the alternative property descriptor set.
To solve this problem, it would be good to have a class that readily implements ITypedList as well as (several of) the common list related interfaces, such as IList, IList<T> or IBindingList. Looks easy at a glance, but the devil is in the details. Basically, for different types of collections we need to have different wrappers. Why? Two major reasons:
- Assuming I had a wrapper class that implements IList<T>, but my wrapped class has only IList. What do we do? Make a lot of assumptions and implement a type-safe version of the interface, which the original class didn’t even offer? That brings a lot of problems of its own and it’s really not the job of the wrapper.
- The class BindingList<T> implements IBindingList, sure. But, although IBindingList requires only IList, not IList<T>, BindingList<T> derives from Collection<T>, which implements IList<T> as well. To make things more interesting, BindingList<T> also implements two additional interfaces, ICancelAddNew and IRaiseItemChangedEvents. So assuming we had a “generic” wrapper that could deal with IBindingList, this wrapper would not implement the other two interfaces, and probably also not IList<T>, thereby removing functionality from the class once it was wrapped. But we can’t just go and implement all the interfaces in the wrapper class – once an interface is implemented, callers will assume it can be used.
As the second point shows, it is not possible to just look at the type that we want to wrap and dynamically decide what to do – at least we can’t do that from within the wrapper class. We can modify the behaviour, but we can’t decide at runtime which interfaces our class implements or not.
Excursion: Theoretically it might be possible to do exactly what I just said we couldn’t do. We could emit IL for a dynamically created class that would implement exactly those interfaces that we need. Rather an elegant approach, but also pretty difficult to implement and maintain. I decided against going that way, as the number of required wrapper classes seems limited.
I have created a small class library that contains four different wrapper classes – one each for IList, IList<T> and IBindingList, and a special one for BindingList<T>. I will make the source code of that library available shortly, together with some usage instructions. Let me know if you have any questions or suggestions.