diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 7d53b7a8da7630..fd7082f4a6764e 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -230,6 +230,10 @@ public static partial class MemoryExtensions public static int BinarySearch(this System.Span span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer, allows ref struct { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int BinarySearch(this System.Span span, TComparable comparable) where TComparable : System.IComparable, allows ref struct { throw null; } + public static T? Max(this System.ReadOnlySpan span) { throw null; } + public static T? Max(this System.ReadOnlySpan span, System.Collections.Generic.IComparer? comparer) { throw null; } + public static T? Min(this System.ReadOnlySpan span) { throw null; } + public static T? Min(this System.ReadOnlySpan span, System.Collections.Generic.IComparer? comparer) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int CommonPrefixLength(this System.Span span, System.ReadOnlySpan other) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs new file mode 100644 index 00000000000000..bae15102aed304 --- /dev/null +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void MinMax_Empty_NonNullableValueType_Throws() + { + ReadOnlySpan span = ReadOnlySpan.Empty; + IComparer nonDefaultComparer = Comparer.Create((x, y) => x.CompareTo(y)); + + TestHelpers.AssertThrows(span, _ => _.Min()); + TestHelpers.AssertThrows(span, _ => _.Max()); + TestHelpers.AssertThrows(span, _ => _.Min(Comparer.Default)); + TestHelpers.AssertThrows(span, _ => _.Max(Comparer.Default)); + TestHelpers.AssertThrows(span, _ => _.Min(nonDefaultComparer)); + TestHelpers.AssertThrows(span, _ => _.Max(nonDefaultComparer)); + } + + [Fact] + public static void MinMax_Empty_ReferenceAndNullableValueType_ReturnsNull() + { + ReadOnlySpan strings = ReadOnlySpan.Empty; + ReadOnlySpan nullableInts = ReadOnlySpan.Empty; + + Assert.Null(strings.Min()); + Assert.Null(strings.Max()); + Assert.Null(nullableInts.Min()); + Assert.Null(nullableInts.Max()); + + Assert.Null(strings.Min(comparer: null)); + Assert.Null(strings.Max(comparer: null)); + Assert.Null(nullableInts.Min(comparer: null)); + Assert.Null(nullableInts.Max(comparer: null)); + } + + [Fact] + public static void MinMax_AllNull_ReturnsNull() + { + ReadOnlySpan strings = new string?[] { null, null, null }; + ReadOnlySpan nullableInts = new int?[] { null, null, null }; + + Assert.Null(strings.Min()); + Assert.Null(strings.Max()); + Assert.Null(nullableInts.Min()); + Assert.Null(nullableInts.Max()); + } + + [Fact] + public static void MinMax_NullNotFirst_NullIgnoredForComparison() + { + ReadOnlySpan strings = new string?[] { "charlie", null, "bravo", null, "delta" }; + ReadOnlySpan nullableInts = new int?[] { 4, null, -1, null, 7 }; + + Assert.Equal("bravo", strings.Min()); + Assert.Equal("delta", strings.Max()); + Assert.Equal(-1, nullableInts.Min()); + Assert.Equal(7, nullableInts.Max()); + } + + [Fact] + public static void MinMax_DefaultComparer_ProducesExpectedValues() + { + ReadOnlySpan ints = new int[] { 4, -1, 7, 2 }; + ReadOnlySpan strings = new string?[] { null, "charlie", "bravo", null, "delta" }; + + Assert.Equal(-1, ints.Min()); + Assert.Equal(7, ints.Max()); + + Assert.Equal("bravo", strings.Min()); + Assert.Equal("delta", strings.Max()); + } + + [Fact] + public static void MinMax_CustomComparer_IsUsed() + { + ReadOnlySpan ints = new int[] { 4, -1, 7, 2 }; + IComparer reverse = Comparer.Create((left, right) => right.CompareTo(left)); + + Assert.Equal(7, ints.Min(reverse)); + Assert.Equal(-1, ints.Max(reverse)); + } + + [Fact] + public static void MinMax_IntegerTypes_DefaultAndNullComparer_ProduceExpectedValues() + { + AssertMinMaxValues(new byte[] { 12, 3, 255, 7 }, (byte)3, (byte)255); + AssertMinMaxValues(new sbyte[] { 12, -9, 100, 7 }, (sbyte)-9, (sbyte)100); + AssertMinMaxValues(new ushort[] { 12, 3, 65535, 7 }, (ushort)3, ushort.MaxValue); + AssertMinMaxValues(new short[] { 12, -9, 100, 7 }, (short)-9, (short)100); + AssertMinMaxValues(new char[] { 'x', 'b', 'm', 'z' }, 'b', 'z'); + AssertMinMaxValues(new uint[] { 12u, 3u, 400u, 7u }, 3u, 400u); + AssertMinMaxValues(new int[] { 12, -9, 400, 7 }, -9, 400); + AssertMinMaxValues(new ulong[] { 12ul, 3ul, 400ul, 7ul }, 3ul, 400ul); + AssertMinMaxValues(new long[] { 12L, -9L, 400L, 7L }, -9L, 400L); + AssertMinMaxValues(new nuint[] { (nuint)12, (nuint)3, (nuint)400, (nuint)7 }, (nuint)3, (nuint)400); + AssertMinMaxValues(new nint[] { (nint)12, (nint)(-9), (nint)400, (nint)7 }, (nint)(-9), (nint)400); + AssertMinMaxValues(new Int128[] { (Int128)12, (Int128)(-9), (Int128)400, (Int128)7 }, (Int128)(-9), (Int128)400); + AssertMinMaxValues(new UInt128[] { (UInt128)12, (UInt128)3, (UInt128)400, (UInt128)7 }, (UInt128)3, (UInt128)400); + } + + [Theory] + [InlineData(float.NaN, 1f, 2f)] + [InlineData(1f, float.NaN, 2f)] + [InlineData(1f, 2f, float.NaN)] + public static void MinMax_Float_NaN_UsesExpectedOrdering(float first, float second, float third) + { + float[] values = [first, second, third]; + ReadOnlySpan span = values; + + Assert.True(float.IsNaN(span.Min())); + Assert.True(float.IsNaN(span.Min(comparer: null))); + Assert.Equal(2f, span.Max()); + Assert.Equal(2f, span.Max(comparer: null)); + } + + [Theory] + [InlineData(double.NaN, 1d, 2d)] + [InlineData(1d, double.NaN, 2d)] + [InlineData(1d, 2d, double.NaN)] + public static void MinMax_Double_NaN_UsesExpectedOrdering(double first, double second, double third) + { + double[] values = [first, second, third]; + ReadOnlySpan span = values; + + Assert.True(double.IsNaN(span.Min())); + Assert.True(double.IsNaN(span.Min(comparer: null))); + Assert.Equal(2d, span.Max()); + Assert.Equal(2d, span.Max(comparer: null)); + } + + private static void AssertMinMaxValues(T[] values, T expectedMin, T expectedMax) + where T : IComparable + { + ReadOnlySpan span = values; + IComparer comparer = Comparer.Create(static (left, right) => left.CompareTo(right)); + IComparer reverseComparer = Comparer.Create(static (left, right) => right.CompareTo(left)); + + Assert.Equal(expectedMin, span.Min()); + Assert.Equal(expectedMax, span.Max()); + Assert.Equal(expectedMin, span.Min(comparer: null)); + Assert.Equal(expectedMax, span.Max(comparer: null)); + Assert.Equal(expectedMin, span.Min(comparer)); + Assert.Equal(expectedMax, span.Max(comparer)); + Assert.Equal(expectedMax, span.Min(reverseComparer)); + Assert.Equal(expectedMin, span.Max(reverseComparer)); + } + } +} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index 5ef352b8d1f6d9..33e76aacc5e7c7 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -30,6 +30,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index ac51112ce4dbd1..38ae696ec23048 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4486,4 +4486,7 @@ The number styles AllowHexSpecifier and AllowBinarySpecifier are not supported on the decimal type. + + Sequence contains no elements. + diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 2ef440179364ef..ece809a274ca5b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -4312,6 +4313,335 @@ public static int BinarySearch( return BinarySearch(span, comparable); } + /// + /// Returns the minimum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the minimum value of. + /// The minimum value in the span. + /// is empty and is a non-nullable value type. + /// + /// If type implements , the method uses that implementation to compare values. Otherwise, if type implements , that implementation is used to compare values. + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the minimum value. If the span contains at least one non-null value, the minimum of those values is returned. If the span does not contain any non-null values, is returned. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? Min(this ReadOnlySpan span) => + Min(span, comparer: null); + + /// + /// Returns the minimum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the minimum value of. + /// The to compare values. + /// The minimum value in the span. + /// is empty and is a non-nullable value type. + /// + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the minimum value. If the span contains at least one non-null value, the minimum of those values is returned. If the span does not contain any non-null values, is returned. + /// + public static T? Min(this ReadOnlySpan span, IComparer? comparer) + { + comparer ??= Comparer.Default; + + if (typeof(T) == typeof(byte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(sbyte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ushort) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(short) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(char) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(uint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(int) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ulong) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(long) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nuint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(Int128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(UInt128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + + return MinMax(span, comparer); + } + + /// + /// Returns the maximum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the maximum value of. + /// The maximum value in the span. + /// is empty and is a non-nullable value type. + /// + /// If type implements , the method uses that implementation to compare values. Otherwise, if type implements , that implementation is used to compare values. + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the maximum value. If the span contains at least one non-null value, the maximum of those values is returned. If the span does not contain any non-null values, is returned. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? Max(this ReadOnlySpan span) => + Max(span, comparer: null); + + /// + /// Returns the maximum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the maximum value of. + /// The to compare values. + /// The maximum value in the span. + /// is empty and is a non-nullable value type. + /// + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the maximum value. If the span contains at least one non-null value, the maximum of those values is returned. If the span does not contain any non-null values, is returned. + /// + public static T? Max(this ReadOnlySpan span, IComparer? comparer) + { + comparer ??= Comparer.Default; + + if (typeof(T) == typeof(byte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(sbyte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ushort) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(short) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(char) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(uint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(int) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ulong) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(long) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nuint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(Int128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(UInt128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + + return MinMax(span, comparer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan Cast(ReadOnlySpan span) + where TTo : struct + => MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length); + + private interface IMinMaxDirection + { + static abstract bool CompareResult(int comparison); + } + + private readonly struct MinDirection : IMinMaxDirection + { + public static bool CompareResult(int comparison) => comparison < 0; + } + + private readonly struct MaxDirection : IMinMaxDirection + { + public static bool CompareResult(int comparison) => comparison > 0; + } + + private static T? MinMax(this ReadOnlySpan span, IComparer comparer) + where TDirection : struct, IMinMaxDirection + { + T? value = default; + int i = 0; + + if (value is null) + { + do + { + if (i >= span.Length) + { + return value; + } + + value = span[i++]; + } + while (value is null); + + while (i < span.Length) + { + T next = span[i++]; + if (next is not null && TDirection.CompareResult(comparer.Compare(next, value))) + { + value = next; + } + } + } + else + { + if (i >= span.Length) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); + } + + value = span[i++]; + if (comparer == Comparer.Default) + { + while (i < span.Length) + { + T next = span[i++]; + if (TDirection.CompareResult(Comparer.Default.Compare(next, value))) + { + value = next; + } + } + } + else + { + while (i < span.Length) + { + T next = span[i++]; + if (TDirection.CompareResult(comparer.Compare(next, value))) + { + value = next; + } + } + } + } + + return value; + } + + private interface IMinMaxCalc where T : struct, IBinaryInteger + { + public static abstract bool Compare(T left, T right); + public static abstract Vector128 Compare(Vector128 left, Vector128 right); + public static abstract Vector256 Compare(Vector256 left, Vector256 right); + public static abstract Vector512 Compare(Vector512 left, Vector512 right); + } + + private readonly struct MinCalc : IMinMaxCalc where T : struct, IBinaryInteger + { + public static bool Compare(T left, T right) => left < right; + public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Min(left, right); + public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Min(left, right); + public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Min(left, right); + } + + private readonly struct MaxCalc : IMinMaxCalc where T : struct, IBinaryInteger + { + public static bool Compare(T left, T right) => left > right; + public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Max(left, right); + public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Max(left, right); + public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Max(left, right); + } + + private static T MinMaxInteger(this ReadOnlySpan span) + where T : struct, IBinaryInteger + where TMinMax : IMinMaxCalc + { + T value; + + if (span.IsEmpty) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); + } + + if (!Vector128.IsHardwareAccelerated || !Vector128.IsSupported || span.Length < Vector128.Count) + { + value = span[0]; + for (int i = 1; i < span.Length; i++) + { + if (TMinMax.Compare(span[i], value)) + { + value = span[i]; + } + } + return value; + } + + // All vectorized paths reduce to 128-bit, so we can use that as our accumulator + // regardless of the maximum supported vector size. + Vector128 best128; + + if (!Vector256.IsHardwareAccelerated || span.Length < Vector256.Count) + { + ReadOnlySpan data = span; + Vector128 best = Vector128.Create(data); + data = data.Slice(Vector128.Count); + + while (data.Length > Vector128.Count) + { + best = TMinMax.Compare(best, Vector128.Create(data)); + data = data.Slice(Vector128.Count); + } + best128 = TMinMax.Compare(best, Vector128.Create(span.Slice(span.Length - Vector128.Count))); + } + else if (!Vector512.IsHardwareAccelerated || span.Length < Vector512.Count) + { + ReadOnlySpan data = span; + Vector256 best = Vector256.Create(data); + data = data.Slice(Vector256.Count); + + while (data.Length > Vector256.Count) + { + best = TMinMax.Compare(best, Vector256.Create(data)); + data = data.Slice(Vector256.Count); + } + best = TMinMax.Compare(best, Vector256.Create(span.Slice(span.Length - Vector256.Count))); + + // Reduce to 128-bit + best128 = TMinMax.Compare(best.GetLower(), best.GetUpper()); + } + else + { + ReadOnlySpan data = span; + Vector512 best = Vector512.Create(data); + data = data.Slice(Vector512.Count); + + while (data.Length > Vector512.Count) + { + best = TMinMax.Compare(best, Vector512.Create(data)); + data = data.Slice(Vector512.Count); + } + best = TMinMax.Compare(best, Vector512.Create(span.Slice(span.Length - Vector512.Count))); + + // Reduce to 128-bit + Vector256 best256 = TMinMax.Compare(best.GetLower(), best.GetUpper()); + best128 = TMinMax.Compare(best256.GetLower(), best256.GetUpper()); + } + + // Reduce to single value + value = HorizontalMinMax(best128); + + return value; + } + + /// Reduces a to a single element using . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static T HorizontalMinMax(Vector128 x) + where T : struct, IBinaryInteger + where TMinMax : IMinMaxCalc + { + // Perform log2(Vector128.Count) reductions, each combining the vector with a shuffled + // copy of itself so that lane 0 ends up holding the min/max of all original lanes. + if (Vector128.Count == 16) + { + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As()); + } + else if (Vector128.Count == 8) + { + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt16(), + Vector128.Create(4, 5, 6, 7, 0, 1, 2, 3)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt16(), + Vector128.Create(2, 3, 0, 1, 4, 5, 6, 7)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt16(), + Vector128.Create(1, 0, 2, 3, 4, 5, 6, 7)).As()); + } + else if (Vector128.Count == 4) + { + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt32(), Vector128.Create(2, 3, 0, 1)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt32(), Vector128.Create(1, 0, 3, 2)).As()); + } + else + { + Debug.Assert(Vector128.Count == 2); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt64(), Vector128.Create(1, 0)).As()); + } + return x.ToScalar(); + } + /// /// Sorts the elements in the entire using the implementation /// of each element of the diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index cf08add006be22..79b1e6e9aba57a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1252,6 +1252,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.ConcurrentDictionary_ItemKeyIsNull; case ExceptionResource.ConcurrentDictionary_TypeOfValueIncorrect: return SR.ConcurrentDictionary_TypeOfValueIncorrect; + case ExceptionResource.InvalidOperation_NoElements: + return SR.InvalidOperation_NoElements; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1453,5 +1455,6 @@ internal enum ExceptionResource InvalidOperation_IncompatibleComparer, ConcurrentDictionary_ItemKeyIsNull, ConcurrentDictionary_TypeOfValueIncorrect, + InvalidOperation_NoElements, } }