You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Migrated from here, I summed up some of the comments there into this post, but you might find it interesting to go through them anyway.
Description
I often bump into scenarios where I have to evaluate an IEnumerable that might be either ICollection<T>, ICollection, IEnumerable<T> or IEnumerable, to get their size if available, so to avoid iterating on them to count them.
The thing with ICollection<T>, is that it doesn't inherit from ICollection, and there are types that inherit from ICollection<T> but not from ICollection (for instance HashSet<T>).
Additionally, ICollection<T> isn't co-variant, meaning not every ICollection<T> is an ICollection<object>, like IEnumerable<T> is.
I'm not arguing about this design, which is a good thing to avoid confusion and mis-adding of types.
Motives
The thing I do find annoying here, and I bump into it a lot, is the Count property, which is common to ICollection and ICollection<T> and IReadOnlyCollection<T>, but these interfaces don't overlap each other.
Obtain a value from a generic type when the type argument is unknown at compile time is only possible with reflection. So in order to get the known size of a collection you'd have to do the following (see discussion here):
//gets count if knownint?GetCountIfKnown(IEnumerablecollection){if(collection is ICollection collection)return collection.Count;else{TypegenericCol= value.GetType().GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition()==typeof(ICollection<>));if(genericCol!=null)
retun (int)genericCol.GetProperty("Count").GetValue(value);}returnnull;}
As pointed out later in comments by @NightOwl888, the IReadOnlyCollection<T> interface also exposes the Count property, but the reasons it doesn't address my issue is:
It's generic, so when the type is unknown at compile time, we can only evaluate it using reflection
ICollection<T> doesn't inherit from it.
Not all collections with known size inherit from it
There are many use cases of non-generic collections I bump into, the real reason we need non-generic support, is not because their not generic, but rather because their generic argument may not be known at runtime.
I can't remember all of the scenarios I've encountered this demand as they are many, but I'll name a few.
When developing general tools (i.e. XAML converters) that are supposed to eat a wide variety of value types and work differently when the value is a collection.
We need to store a collection of ICollection<T>s of a variety of types. When enumerating the main collection, the count of the items can only be achieved by acquiring the collection type with reflection.
Solution
My suggestion is then, to introduce a new interface ICountable (or IReadOnlyCollection - non generic) that its contract determines that a collection has a Count property and exposed the collection size regardless of its item type.
This interface should be implemented by (please comment if you're aware of others):
ICollection
ICollection<T>
IReadOnlyCollection<T>
The new interface should have one sole task: expose a known size of a collection via a Count property, with disregard to the collection type (T), editability (ICollection vs IReadOnlyCollection), or other features.
int?GetCountIfKnown(IEnumerableenumerable)=>(enumerable as IReadOnlyCollection)?.Count;
Migrated from here, I summed up some of the comments there into this post, but you might find it interesting to go through them anyway.
Description
I often bump into scenarios where I have to evaluate an
IEnumerable
that might be eitherICollection<T>
,ICollection
,IEnumerable<T>
orIEnumerable
, to get their size if available, so to avoid iterating on them to count them.The thing with
ICollection<T>
, is that it doesn't inherit fromICollection
, and there are types that inherit fromICollection<T>
but not fromICollection
(for instanceHashSet<T>
).Additionally,
ICollection<T>
isn't co-variant, meaning not everyICollection<T>
is anICollection<object>
, likeIEnumerable<T>
is.I'm not arguing about this design, which is a good thing to avoid confusion and mis-adding of types.
Motives
The thing I do find annoying here, and I bump into it a lot, is the
Count
property, which is common toICollection
andICollection<T>
andIReadOnlyCollection<T>
, but these interfaces don't overlap each other.Obtain a value from a generic type when the type argument is unknown at compile time is only possible with reflection. So in order to get the known size of a collection you'd have to do the following (see discussion here):
As pointed out later in comments by @NightOwl888, the
IReadOnlyCollection<T>
interface also exposes theCount
property, but the reasons it doesn't address my issue is:ICollection<T>
doesn't inherit from it.There are many use cases of non-generic collections I bump into, the real reason we need non-generic support, is not because their not generic, but rather because their generic argument may not be known at runtime.
I can't remember all of the scenarios I've encountered this demand as they are many, but I'll name a few.
ICollection<T>
s of a variety of types. When enumerating the main collection, the count of the items can only be achieved by acquiring the collection type with reflection.Solution
My suggestion is then, to introduce a new interface
ICountable
(orIReadOnlyCollection
- non generic) that its contract determines that a collection has aCount
property and exposed the collection size regardless of its item type.This interface should be implemented by (please comment if you're aware of others):
ICollection
ICollection<T>
IReadOnlyCollection<T>
The new interface should have one sole task: expose a known size of a collection via a
Count
property, with disregard to the collection type (T
), editability (ICollection
vsIReadOnlyCollection
), or other features.Related: https://github.com/dotnet/corefx/issues/23700.
The text was updated successfully, but these errors were encountered: