Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make a new interface that exposes collection count #8841

Closed
weitzhandler opened this issue Aug 31, 2017 · 0 comments
Closed

Make a new interface that exposes collection count #8841

weitzhandler opened this issue Aug 31, 2017 · 0 comments

Comments

@weitzhandler
Copy link
Contributor

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 known
int? GetCountIfKnown(IEnumerable collection)
{
  if(collection is ICollection collection)
    return collection.Count;
  else
  {
    Type genericCol = value.GetType().GetInterfaces().FirstOrDefault(i => 
      i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>)); 
    if (genericCol != null)
      retun (int)genericCol.GetProperty("Count").GetValue(value);
  }  
  return null;
}

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(IEnumerable enumerable) =>
  (enumerable as IReadOnlyCollection)?.Count;

Related: https://github.com/dotnet/corefx/issues/23700.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant