Is it good practice to return an IEnumerable<T> for a collection that implements INotifyCollectionChanged
Writing another question for SO, I came to a pattern that I use very often and I never really reflected about. But now, I’m no longer sure if this is the right way:
If I have collections that my WPF-controls will bind to, I returned almost always IEnumerable<SomeType>
. However internally , this is in most cases an ReadOnlyObservableCollection<SomeType>
. I never had a problem with this and all consuming controls always updated correctly, what is not astonishing because they check for the INotifyCollectionChanged
-interface.
But my question is now, if this is bad practice to declare in the signature only the IEnumerable<SomeType>
but to return (and also de开发者_如何转开发pend on) something much more powerful (INotifyCollectionChanged
).
Update:
I try to clarify:
My main intention is to return an IEnumerable<SomeType>
. But most of the time, the returned IEnumerable<SomeType>
implements also INotifyCollectionChanged
such as ReadOnlyObservableCollection<SomeType>
does. The consuming controls bind accordingly (what my second intention is).
Maybe I should have asked: Is there an interface that exactly contains IEnumerable and INotifyPropertyChanged.
Remember, IEnumerable<T>
and INotifyCollectionChanged
are both interfaces - they aren't a definitive type. You can design your concrete class to implement both, with no problems. Your API can return the appropriate interface for the method call. This is, in fact, a good design - not something to avoid.
ObservableCollection<T>
does this (indirectly via Collection<T>
), and also implements IList<T>
as well as other interfaces.
If you are making your own custom collection, and plan to use it with data binding, I would make it implement IEnumerable<T>
(or potentially IList<T>
, if appropriate) and INotifyCollectionChanged
. This will give it the best usability, both from code, but also for efficient binding.
(and also depend on)
However, depending on an interface to exist that isn't part of the API is bad practice. This is dangerous, and part of the reason to return an interface instead of a concrete type is to allow the implementation to change later. By putting a dependency on an interface that isn't declared, you're making your code fragile.
That being said, I often do what you're attempting, but it's not a "hard" dependency. Rather, I use IEnumerable<T>
, and check for INotifyCollectionChanged
- taking advantage of it if it is implemented. I do, however, allow the code to work if the "secondary" interface doesn't exist.
I would tend to think that if only INotifyCollectionChanged
is correct, you don't want to return just an IEnumerable
This would be very much like returning Object
where what you really want is MemoryStream.
Sure, you can convert back to the type you need, but what if some one comes along later, see's the return type of IEnumerable
and writes code that matches that return but does not implement INotifyCollectionChanged
?
Returning a minimal interface instead of a richer one is often a good idea. If you have a rich interface there are more things to test and the more things that can go wrong. It also means that you will have to always to support all these extra features for the foreseeable future if you want to avoid breaking changes. This makes it harder to modify your implementation later.
However depending on something that isn't guaranteed by the interface is a bad practice. If one day you don't get an ObservableCollection
but some other type of collection then the code will fail at runtime rather than at compile time.
Forget about WPF's data binding, and then deside what is the best for return, IEnumerable<T>
or more concrette type. Since WPF uses reflection to determine the best way of how to bind to specified collection, you shouldn't worry about.
if this is bad practice to declare in the signature only the
IEnumerable<SomeType>
but to return (and also depend on) something much more powerful (INotifyCollectionChanged
)
The return value type is chosen to allow value consumers to work with the value without unsafe type casting.
- If
IEnumerable
is returned then value consumer should not make assumptions whether it is a collection or it is a collection that can change or it is a collection that can change and can notify about it's changes.
The only way to use IEnumerable
is to enumerate it! There is no way to determine that something changes and it should be enumerated again!
- If
ReadOnlyObservableCollection
is returned then value consumer is free to assume that it is a collection and it can notify about it's changes.
ReadOnlyObservableCollection
value consumer can enumerate the collection or can subscribe for change notifications and later enumerate the collection again when notification is received.
It is good practice to return some complex collection under IEnumerable
interface.
It is bad practice to use IEnumerable
as INotifyPropertyChanged
or convert IEnumerable
into INotifyPropertyChanged
or something more complex.
Is there an interface that exactly contains
IEnumerable
andINotifyPropertyChanged
Unfortunately no such interface, only class ReadOnlyObservableCollection
.
精彩评论