Skip to content

[Breaking change]: Fix type of the IComparer<T> on System.Linq.Queryable MinBy and MaxBy #45535

@IDisposable

Description

@IDisposable

Description

Change the generic type of the IComparer used in the MinBy and MaxBy in System.Linq.Queryable to the correct TKey instead of the current (and wrong) TSource.

Specifically, change wrong use of IComparer<TSource> for comparer

public static TSource? MinBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TSource>? comparer)

public static TSource? MaxBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TSource>? comparer)

to the correct IComparer<TKey> for comparer

public static TSource? MinBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TKey>? comparer)

public static TSource? MaxBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TKey>? comparer)

Version

.NET 10 Preview 4

Previous behavior

The versions of MaxBy and MinBy in System.Linq.Queryable that accepted a custom comparer were declared as taking an IComparer<TSource> when they SHOULD have been IComparer<TKey> to match what the keySelector was projecting.

Prior attempts to use a custom comparer would have only worked if TSource and TKey were the same type (e.g. both string or int). Other type combinations would result in an System.IndexOutOfRangeException as documented here in Dotnet Runtime Issue #113878 because the correct backing MaxBy or backing MinBy Method could not be located.

Note, this was never a compile-time error, it would only fail at runtime.

Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Linq.EnumerableRewriter.FindEnumerableMethodForQueryable(String name, ReadOnlyCollection`1 args, Type[] typeArgs)
   at System.Linq.EnumerableRewriter.VisitMethodCall(MethodCallExpression m)
   at System.Linq.EnumerableExecutor`1.Execute()
   at System.Linq.EnumerableQuery`1.System.Linq.IQueryProvider.Execute[TElement](Expression expression)
   at System.Linq.Queryable.MaxBy[TSource,TKey](IQueryable`1 source, Expression`1 keySelector, IComparer`1 comparer)
   at Program.<Main>$(String[] args) in C:\Users\eitsarpa\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:line 1

Examples of this working correctly the TSource and TKey types are the both same type:

New behavior

You can use a different type for TSource and TKey in the MaxBy and MinBy that accepted a custom comparer.

This means that any use will work correctly (as long as there is an IComparer<TKey>) and will no longer throw an System.IndexOutOfRangeException.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

The original implementation was incorrect see comment on Dotnet Runtime Issue #113878 as specified here and implemented here

Recommended action

None required, things will just quietly work now.

Feature area

LINQ

Affected APIs

System.Linq.Queryable.MinBy and System.Linq.Queryable.MaxBy Linq extension methods.

Metadata

Metadata

Labels

⌚ Not TriagedNot triagedbreaking-changeIndicates a .NET Core breaking changein-prThis issue will be closed (fixed) by an active pull request.

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions