Skip to content

Conversation

@DustinCampbell
Copy link
Member

This adds a handful of new SelectXXXAsArray extension methods and overloads. In particular, there are new SelectAndOrderXXXAsArray methods that transform elements and order them in a single pass.

Add helpers that combine Select And Order/OrderBy/OrderDescending/OrderByDescending to avoid a common LINQ anti-pattern:

```C#
List<int> list = [1, 2, 3, 4, 5];
var result = list.Select(x => x * 2).OrderDescendingAsArray();
```

The code above results in extra allocations by the LINQ Select(...) method, even though the expected results in an ImmutableArray<int>. With these helpers in place, the code above can be written more efficiently as:

```C#
List<int> list = [1, 2, 3, 4, 5];
var result = list.SelectAndOrderDescendingAsArray(x => x * 2);
```
LINQ provides a Select(...) overload where the selector takes an int representing the item index. This change adds a similar overload for SelectAsArray.
Use TryGetCount(...) to retrieve a capacity up front before creating a PooledArrayBuilder<T>.
The type tests for ImmutableArray<T> and IReadOnlyList<T> aren't needed in SelectAndOrderXXXAsArray helpers, since those tests are already performed by their calls to SelectAsArray.
@DustinCampbell
Copy link
Member Author

@dotnet/razor-compiler: Please take a look. Much of these changes are additions to shared code. However, there are some changes in shared methods used by the compiler (e.g. SelectAsArray).

@ToddGrun pointed out that the IReadOnlyList<T>.SelectAsArray(...) extension methods really didn't need the complexity of pattern matching and a PooledArrayBuilder<T>. Because we have an IReadOnlyList<T>, we already know the length and can construct the final array up front. This commit makes that change and similarly adjusts the IReadOnlyList<T>.ToArray(), ImmutableArray<T>.SelectAsArray(), and IEnumerable<T>.SelectAsArray() extension methods. Comments have been added throughout to explain specific decisions.
result[index++] = selector(item);
}

Debug.Assert(result.Length == count);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert(result.Length == count);

nit: Consider adding a Debug.Assert inside the loop that index isn't too large

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An IndexOutOfRangeException would already be thrown in that case, since result is created with a length of count.

Copy link
Contributor

@ToddGrun ToddGrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@DustinCampbell DustinCampbell merged commit 85ff5c3 into dotnet:main May 5, 2025
11 checks passed
@DustinCampbell DustinCampbell deleted the select-and-order-helpers branch May 5, 2025 17:47
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone May 5, 2025
@RikkiGibson RikkiGibson modified the milestones: Next, 18.0 P1 Aug 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants