Skip to content

Commit aac10a3

Browse files
committed
Update to keep the old MinBy and MaxBy around as Obsolete
Ensures the binary and source compatibility in case anyone was using the broken overloads.
1 parent f5286f7 commit aac10a3

File tree

6 files changed

+74
-37
lines changed

6 files changed

+74
-37
lines changed

docs/project/list-of-diagnostics.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ The PR that reveals the implementation of the `<IncludeInternalObsoleteAttribute
115115
| __`SYSLIB0058`__ | KeyExchangeAlgorithm, KeyExchangeStrength, CipherAlgorithm, CipherStrength, HashAlgorithm and HashStrength properties of SslStream are obsolete. Use NegotiatedCipherSuite instead. |
116116
| __`SYSLIB0059`__ | SystemEvents.EventsThreadShutdown callbacks are not run before the process exits. Use AppDomain.ProcessExit instead. |
117117
| __`SYSLIB0060`__ | The constructors on Rfc2898DeriveBytes are obsolete. Use the static Pbkdf2 method instead. |
118+
| __`SYSLIB0061`__ | The Queryable MinBy and MaxBy taking an IComparer<TSource> are obsolete. Use the new ones that take an IComparer<TKey>. |
118119

119120
## Analyzer Warnings
120121

src/libraries/Common/src/System/Obsoletions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ internal static class Obsoletions
192192
internal const string Rfc2898DeriveBytesCtorMessage = "The constructors on Rfc2898DeriveBytes are obsolete. Use the static Pbkdf2 method instead.";
193193
internal const string Rfc2898DeriveBytesCtorDiagId = "SYSLIB0060";
194194

195+
internal const string QueryableMinByMaxByTSourceObsoleteMessage = "The Queryable MinBy and MaxBy taking an IComparer<TSource> are obsolete. Use the new ones that take an IComparer<TKey>.";
196+
internal const string QueryableMinByMaxByTSourceObsoleteDiagId = "SYSLIB0061";
197+
195198
// When adding a new diagnostic ID, add it to the table in docs\project\list-of-diagnostics.md as well.
196199
// Keep new const identifiers above this comment.
197200
}

src/libraries/System.Linq.Queryable/ref/System.Linq.Queryable.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,19 @@ public static partial class Queryable
128128
public static long LongCount<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
129129
public static long LongCount<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
130130
public static TSource? MaxBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
131+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
132+
[System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)]
133+
[System.ObsoleteAttribute("The Queryable MinBy and MaxBy taking an IComparer<TSource> are obsolete. Use the new ones that take an IComparer<TKey>.", DiagnosticId="SYSLIB0061", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
134+
public static TSource? MaxBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
131135
public static TSource? MaxBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
132136
public static TSource? Max<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
133137
public static TSource? Max<TSource>(this System.Linq.IQueryable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
134138
public static TResult? Max<TSource, TResult>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TResult>> selector) { throw null; }
135139
public static TSource? MinBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
140+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
141+
[System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)]
142+
[System.ObsoleteAttribute("The Queryable MinBy and MaxBy taking an IComparer<TSource> are obsolete. Use the new ones that take an IComparer<TKey>.", DiagnosticId="SYSLIB0061", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
143+
public static TSource? MinBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
136144
public static TSource? MinBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
137145
public static TSource? Min<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
138146
public static TSource? Min<TSource>(this System.Linq.IQueryable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }

src/libraries/System.Linq.Queryable/src/System.Linq.Queryable.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@
2323
<Reference Include="System.Runtime" />
2424
</ItemGroup>
2525

26+
<ItemGroup>
27+
<Compile Include="$(CommonPath)System\Obsoletions.cs"
28+
Link="Common\System\Obsoletions.cs" />
29+
</ItemGroup>
30+
2631
</Project>

src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
using System.Collections;
55
using System.Collections.Generic;
6+
using System.ComponentModel;
67
using System.Diagnostics.CodeAnalysis;
78
using System.Linq.Expressions;
9+
using System.Runtime.CompilerServices;
810

911
namespace System.Linq
1012
{
@@ -1775,6 +1777,33 @@ public static long LongCount<TSource>(this IQueryable<TSource> source, Expressio
17751777
Expression.Quote(keySelector)));
17761778
}
17771779

1780+
/// <summary>Returns the minimum value in a generic <see cref="IQueryable{T}"/> according to a specified key selector function.</summary>
1781+
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
1782+
/// <typeparam name="TKey">The type of key to compare elements by.</typeparam>
1783+
/// <param name="source">A sequence of values to determine the minimum value of.</param>
1784+
/// <param name="keySelector">A function to extract the key for each element.</param>
1785+
/// <param name="comparer">The <see cref="IComparer{TSource}" /> to compare elements.</param>
1786+
/// <returns>The value with the minimum key in the sequence.</returns>
1787+
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
1788+
/// <exception cref="ArgumentException">No key extracted from <paramref name="source" /> implements the <see cref="IComparable" /> or <see cref="IComparable{TSource}" /> interface.</exception>
1789+
[DynamicDependency("MinBy`2", typeof(Enumerable))]
1790+
[Obsolete(Obsoletions.QueryableMinByMaxByTSourceObsoleteMessage, DiagnosticId=Obsoletions.QueryableMinByMaxByTSourceObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
1791+
[EditorBrowsable(EditorBrowsableState.Never)]
1792+
[OverloadResolutionPriority(-1)]
1793+
public static TSource? MinBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TSource>? comparer)
1794+
{
1795+
ArgumentNullException.ThrowIfNull(source);
1796+
ArgumentNullException.ThrowIfNull(keySelector);
1797+
1798+
return source.Provider.Execute<TSource>(
1799+
Expression.Call(
1800+
null,
1801+
new Func<IQueryable<TSource>, Expression<Func<TSource, TKey>>, IComparer<TSource>, TSource?>(MinBy).Method,
1802+
source.Expression,
1803+
Expression.Quote(keySelector),
1804+
Expression.Constant(comparer, typeof(IComparer<TSource>))));
1805+
}
1806+
17781807
/// <summary>Returns the minimum value in a generic <see cref="IQueryable{T}"/> according to a specified key selector function.</summary>
17791808
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
17801809
/// <typeparam name="TKey">The type of key to compare elements by.</typeparam>
@@ -1865,6 +1894,33 @@ public static long LongCount<TSource>(this IQueryable<TSource> source, Expressio
18651894
Expression.Quote(keySelector)));
18661895
}
18671896

1897+
/// <summary>Returns the maximum value in a generic <see cref="IQueryable{T}"/> according to a specified key selector function.</summary>
1898+
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
1899+
/// <typeparam name="TKey">The type of key to compare elements by.</typeparam>
1900+
/// <param name="source">A sequence of values to determine the maximum value of.</param>
1901+
/// <param name="keySelector">A function to extract the key for each element.</param>
1902+
/// <param name="comparer">The <see cref="IComparer{TSource}" /> to compare elements.</param>
1903+
/// <returns>The value with the maximum key in the sequence.</returns>
1904+
/// <exception cref="ArgumentNullException"><paramref name="source" /> is <see langword="null" />.</exception>
1905+
/// <exception cref="ArgumentException">No key extracted from <paramref name="source" /> implements the <see cref="IComparable" /> or <see cref="IComparable{TSource}" /> interface.</exception>
1906+
[DynamicDependency("MaxBy`2", typeof(Enumerable))]
1907+
[Obsolete(Obsoletions.QueryableMinByMaxByTSourceObsoleteMessage, DiagnosticId=Obsoletions.QueryableMinByMaxByTSourceObsoleteDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
1908+
[EditorBrowsable(EditorBrowsableState.Never)]
1909+
[OverloadResolutionPriority(-1)]
1910+
public static TSource? MaxBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TSource>? comparer)
1911+
{
1912+
ArgumentNullException.ThrowIfNull(source);
1913+
ArgumentNullException.ThrowIfNull(keySelector);
1914+
1915+
return source.Provider.Execute<TSource>(
1916+
Expression.Call(
1917+
null,
1918+
new Func<IQueryable<TSource>, Expression<Func<TSource, TKey>>, IComparer<TSource>, TSource?>(MaxBy).Method,
1919+
source.Expression,
1920+
Expression.Quote(keySelector),
1921+
Expression.Constant(comparer, typeof(IComparer<TSource>))));
1922+
}
1923+
18681924
/// <summary>Returns the maximum value in a generic <see cref="IQueryable{T}"/> according to a specified key selector function.</summary>
18691925
/// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam>
18701926
/// <typeparam name="TKey">The type of key to compare elements by.</typeparam>

src/libraries/apicompat/ApiCompatBaseline.NetCoreAppLatestStable.xml

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
33
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
4-
<Suppression>
5-
<DiagnosticId>CP0002</DiagnosticId>
6-
<Target>M:System.Linq.Queryable.MaxBy``2(System.Linq.IQueryable{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Collections.Generic.IComparer{``0})</Target>
7-
<Left>net9.0/netstandard.dll</Left>
8-
<Right>net10.0/netstandard.dll</Right>
9-
</Suppression>
10-
<Suppression>
11-
<DiagnosticId>CP0002</DiagnosticId>
12-
<Target>M:System.Linq.Queryable.MinBy``2(System.Linq.IQueryable{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Collections.Generic.IComparer{``0})</Target>
13-
<Left>net9.0/netstandard.dll</Left>
14-
<Right>net10.0/netstandard.dll</Right>
15-
</Suppression>
16-
<Suppression>
17-
<DiagnosticId>CP0002</DiagnosticId>
18-
<Target>M:System.Linq.Queryable.MaxBy``2(System.Linq.IQueryable{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Collections.Generic.IComparer{``0})</Target>
19-
<Left>net9.0/System.Core.dll</Left>
20-
<Right>net10.0/System.Core.dll</Right>
21-
</Suppression>
22-
<Suppression>
23-
<DiagnosticId>CP0002</DiagnosticId>
24-
<Target>M:System.Linq.Queryable.MinBy``2(System.Linq.IQueryable{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Collections.Generic.IComparer{``0})</Target>
25-
<Left>net9.0/System.Core.dll</Left>
26-
<Right>net10.0/System.Core.dll</Right>
27-
</Suppression>
28-
<Suppression>
29-
<DiagnosticId>CP0002</DiagnosticId>
30-
<Target>M:System.Linq.Queryable.MaxBy``2(System.Linq.IQueryable{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Collections.Generic.IComparer{``0})</Target>
31-
<Left>net9.0/System.Linq.Queryable.dll</Left>
32-
<Right>net10.0/System.Linq.Queryable.dll</Right>
33-
</Suppression>
34-
<Suppression>
35-
<DiagnosticId>CP0002</DiagnosticId>
36-
<Target>M:System.Linq.Queryable.MinBy``2(System.Linq.IQueryable{``0},System.Linq.Expressions.Expression{System.Func{``0,``1}},System.Collections.Generic.IComparer{``0})</Target>
37-
<Left>net9.0/System.Linq.Queryable.dll</Left>
38-
<Right>net10.0/System.Linq.Queryable.dll</Right>
39-
</Suppression>
404
<Suppression>
415
<DiagnosticId>CP0015</DiagnosticId>
426
<Target>M:System.Delegate.#ctor(System.Type,System.String)$0:[T:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute]</Target>
@@ -367,4 +331,4 @@
367331
<Left>net9.0/System.Runtime.dll</Left>
368332
<Right>net10.0/System.Runtime.dll</Right>
369333
</Suppression>
370-
</Suppressions>
334+
</Suppressions>

0 commit comments

Comments
 (0)