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

Linq's TryGetNonEnumeratedCount() should check for IReadOnlyCollection<T> #54764

Closed
KPixel opened this issue Jun 25, 2021 · 7 comments
Closed
Labels
area-System.Linq untriaged New issue has not been triaged by the area owner

Comments

@KPixel
Copy link

KPixel commented Jun 25, 2021

IReadOnlyCollection<T> can provide a Count without enumerating in:

public static bool TryGetNonEnumeratedCount<TSource>(this IEnumerable<TSource> source, out int count)

Note: This request was already mentioned in #52775. However, since that issue started as something much bigger, I am concerned that this specific request will be missed.

@dotnet-issue-labeler dotnet-issue-labeler bot added area-System.Linq untriaged New issue has not been triaged by the area owner labels Jun 25, 2021
@ghost
Copy link

ghost commented Jun 25, 2021

Tagging subscribers to this area: @eiriktsarpalis
See info in area-owners.md if you want to be subscribed.

Issue Details

IReadOnlyCollection<T> can provide a Count without enumerating in:

public static bool TryGetNonEnumeratedCount<TSource>(this IEnumerable<TSource> source, out int count)

Note: This request was already mentioned in #52775. However, since that issue started as something much bigger, I am concerned that this specific request will be missed.

Author: KPixel
Assignees: -
Labels:

area-System.Linq, untriaged

Milestone: -

@huoyaoyuan
Copy link
Member

#42254

@teo-tsirpanis
Copy link
Contributor

This change would regress F#'s lists.

For those that don't know, they are implemented as single-linked lists, and their length is computed in linear time by traversing the entire list. They implement IReadOnlyCollection but not ICollection, which means that with the existing behavior, the TryGetNonEnumeratedCount method does not actually enumerate over the entire collection, which would happen if the method special-cased IReadOnlyCollections.

@eiriktsarpalis
Copy link
Member

This was considered in the implementation PR but ultimately we went with reflecting the checks that exist in Enumerable.Count(). The decision was informed by the fact that most BCL types implementing IReadOnlyCollection<T> usually also implement ICollection<T>. I'm on the fence on this one, it's probably fair to assume that callers of this particular method might prefer one additional runtime check over not knowing the count. @stephentoub thoughts?

@stephentoub
Copy link
Member

stephentoub commented Jun 28, 2021

@stephentoub thoughts?

If we're going to start adding checks for IReadOnlyCollection<T>, I would like us to do so holistically across LINQ; that doesn't mean we need to add such checks everywhere, but we should evaluate all the places we look at ICollection<T> and decide whether a corresponding check for IReadOnlyCollection<T> makes sense, not just in this one method (especially since if this method successfully gives you the count, that might suggest subsequent LINQ operations would also be able to optimize by getting the count as part of the implementation, such that we actually make things worse if it's not the case). Also, historically we shied away from IROC<T> because its covariant nature made it a much more expensive type check to perform. We believe we've reduced those costs significantly, but any decision here would need to factor in the performance impact of the check.

@eiriktsarpalis
Copy link
Member

This change would regress F#'s lists.

For those that don't know, they are implemented as single-linked lists, and their length is computed in linear time by traversing the entire list.

@teo-tsirpanis I see your point but I wouldn't consider it a regression. It is assumed that callers of TryGetNonEnumeratedCount need to know the count of the enumerable ahead of enumeration and the only way to achieve this in F# lists is via linear traversal. Note however that the IROC implementation uses the Length property which is substantially faster compared to using an enumerator. Thus technically speaking, the "NonEnumerated" part of TryGetEnumeratedCount is still being honored since no enumerator is being created.

@eiriktsarpalis
Copy link
Member

If we're going to start adding checks for IReadOnlyCollection, I would like us to do so holistically across LINQ

That's probably covered by #42254. I'm going to close this issue in favor of that.

@ghost ghost locked as resolved and limited conversation to collaborators Jul 28, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-System.Linq untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

5 participants