Skip to content

Commit b790824

Browse files
authored
Add MemoryExtensions overloads with comparer (#110197)
1 parent 9aa8cbf commit b790824

File tree

17 files changed

+2658
-867
lines changed

17 files changed

+2658
-867
lines changed

src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceUtils.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,7 @@ internal static void VerifyAttributes(StringDictionary? attributes, string[]? su
1919

2020
foreach (string key in attributes.Keys)
2121
{
22-
bool found = false;
23-
if (supportedAttributes != null)
24-
{
25-
for (int i = 0; i < supportedAttributes.Length; i++)
26-
{
27-
if (supportedAttributes[i] == key)
28-
found = true;
29-
}
30-
}
31-
32-
if (!found)
22+
if (supportedAttributes is null || !supportedAttributes.Contains(key))
3323
{
3424
throw new ArgumentException(SR.Format(SR.AttributeNotSupported, key, parent.GetType().FullName));
3525
}

src/libraries/System.Linq/src/System/Linq/Contains.cs

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,43 +18,37 @@ public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource v
1818
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
1919
}
2020

21+
if (source.TryGetSpan(out ReadOnlySpan<TSource> span))
22+
{
23+
return span.Contains(value, comparer);
24+
}
25+
2126
if (comparer is null)
2227
{
2328
// While it's tempting, this must not delegate to ICollection<TSource>.Contains, as the historical semantics
2429
// of a null comparer with this method are to use EqualityComparer<TSource>.Default, and that might differ
2530
// from the semantics encoded in ICollection<TSource>.Contains.
2631

27-
// We don't bother special-casing spans here as explicitly providing a null comparer with a known collection type
28-
// is relatively rare. If you don't care about the comparer, you use the other overload, and while it will delegate
29-
// to this overload with a null comparer, it'll only do so for collections from which we can't extract a span.
30-
// And if you do care about the comparer, you're generally passing in a non-null one.
31-
32-
foreach (TSource element in source)
33-
{
34-
if (EqualityComparer<TSource>.Default.Equals(element, value))
35-
{
36-
return true;
37-
}
38-
}
39-
}
40-
else if (source.TryGetSpan(out ReadOnlySpan<TSource> span))
41-
{
42-
foreach (TSource element in span)
32+
if (typeof(TSource).IsValueType)
4333
{
44-
if (comparer.Equals(element, value))
34+
foreach (TSource element in source)
4535
{
46-
return true;
36+
if (EqualityComparer<TSource>.Default.Equals(element, value))
37+
{
38+
return true;
39+
}
4740
}
41+
42+
return false;
4843
}
44+
45+
comparer = EqualityComparer<TSource>.Default;
4946
}
50-
else
47+
foreach (TSource element in source)
5148
{
52-
foreach (TSource element in source)
49+
if (comparer.Equals(element, value))
5350
{
54-
if (comparer.Equals(element, value))
55-
{
56-
return true;
57-
}
51+
return true;
5852
}
5953
}
6054

src/libraries/System.Memory/ref/System.Memory.cs

Lines changed: 35 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using Xunit;
6+
7+
namespace System.SpanTests
8+
{
9+
public static partial class ReadOnlySpanTests
10+
{
11+
private static IEnumerable<IEqualityComparer<T>?> GetDefaultEqualityComparers<T>()
12+
{
13+
yield return null;
14+
15+
yield return EqualityComparer<T>.Default;
16+
17+
yield return EqualityComparer<T>.Create((i, j) => EqualityComparer<T>.Default.Equals(i, j));
18+
19+
if (typeof(T) == typeof(string))
20+
{
21+
yield return (IEqualityComparer<T>)(object)StringComparer.Ordinal;
22+
}
23+
}
24+
25+
private static IEnumerable<IComparer<T>?> GetDefaultComparers<T>()
26+
{
27+
yield return null;
28+
29+
yield return Comparer<T>.Default;
30+
31+
yield return Comparer<T>.Create((i, j) => Comparer<T>.Default.Compare(i, j));
32+
33+
if (typeof(T) == typeof(string))
34+
{
35+
yield return (IComparer<T>)(object)StringComparer.Ordinal;
36+
}
37+
}
38+
39+
private static IEqualityComparer<T> GetFalseEqualityComparer<T>() =>
40+
EqualityComparer<T>.Create((i, j) => false);
41+
}
42+
}

src/libraries/System.Memory/tests/ReadOnlySpan/Contains.T.cs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
45
using Xunit;
56

67
namespace System.SpanTests
@@ -27,13 +28,13 @@ public static void TestContains()
2728
{
2829
a[i] = 10 * (i + 1);
2930
}
30-
ReadOnlySpan<int> span = new ReadOnlySpan<int>(a);
3131

3232
for (int targetIndex = 0; targetIndex < length; targetIndex++)
3333
{
3434
int target = a[targetIndex];
35-
bool found = span.Contains(target);
36-
Assert.True(found);
35+
Assert.True(new ReadOnlySpan<int>(a).Contains(target));
36+
Assert.All(GetDefaultEqualityComparers<int>(), comparer => Assert.True(new ReadOnlySpan<int>(a).Contains(target, comparer)));
37+
Assert.False(new ReadOnlySpan<int>(a).Contains(target, GetFalseEqualityComparer<int>()));
3738
}
3839
}
3940
}
@@ -52,9 +53,9 @@ public static void TestMultipleContains()
5253
a[length - 1] = 5555;
5354
a[length - 2] = 5555;
5455

55-
ReadOnlySpan<int> span = new ReadOnlySpan<int>(a);
56-
bool found = span.Contains(5555);
57-
Assert.True(found);
56+
Assert.True(new ReadOnlySpan<int>(a).Contains(5555));
57+
Assert.All(GetDefaultEqualityComparers<int>(), comparer => Assert.True(new ReadOnlySpan<int>(a).Contains(5555, comparer)));
58+
Assert.False(new ReadOnlySpan<int>(a).Contains(5555, GetFalseEqualityComparer<int>()));
5859
}
5960
}
6061

@@ -71,8 +72,7 @@ public static void OnNoMatchForContainsMakeSureEveryElementIsCompared()
7172
a[i] = new TInt(10 * (i + 1), log);
7273
}
7374
ReadOnlySpan<TInt> span = new ReadOnlySpan<TInt>(a);
74-
bool found = span.Contains(new TInt(9999, log));
75-
Assert.False(found);
75+
Assert.False(span.Contains(new TInt(9999, log)));
7676

7777
// Since we asked for a non-existent value, make sure each element of the array was compared once.
7878
// (Strictly speaking, it would not be illegal for IndexOf to compare an element more than once but
@@ -112,17 +112,19 @@ void checkForOutOfRangeAccess(int x, int y)
112112
}
113113

114114
ReadOnlySpan<TInt> span = new ReadOnlySpan<TInt>(a, GuardLength, length);
115-
bool found = span.Contains(new TInt(9999, checkForOutOfRangeAccess));
116-
Assert.False(found);
115+
Assert.False(span.Contains(new TInt(9999, checkForOutOfRangeAccess)));
117116
}
118117
}
119118

120119
[Fact]
121120
public static void ZeroLengthContains_String()
122121
{
123122
ReadOnlySpan<string> span = new ReadOnlySpan<string>(Array.Empty<string>());
124-
bool found = span.Contains("a");
125-
Assert.False(found);
123+
Assert.False(span.Contains("a"));
124+
Assert.All(GetDefaultEqualityComparers<string>(), comparer => Assert.False(new ReadOnlySpan<string>(Array.Empty<string>()).Contains("a", comparer)));
125+
Assert.False(span.Contains("a", null));
126+
Assert.False(span.Contains("a", EqualityComparer<string>.Default));
127+
Assert.False(span.Contains("a", EqualityComparer<string>.Create((i, j) => i == j)));
126128
}
127129

128130
[Fact]
@@ -140,8 +142,9 @@ public static void TestMatchContains_String()
140142
for (int targetIndex = 0; targetIndex < length; targetIndex++)
141143
{
142144
string target = a[targetIndex];
143-
bool found = span.Contains(target);
144-
Assert.True(found);
145+
Assert.True(span.Contains(target));
146+
Assert.All(GetDefaultEqualityComparers<string>(), comparer => Assert.True(new ReadOnlySpan<string>(a).Contains(target, comparer)));
147+
Assert.False(span.Contains(target, GetFalseEqualityComparer<string>()));
145148
}
146149
}
147150
}
@@ -161,8 +164,7 @@ public static void TestNoMatchContains_String()
161164
}
162165
ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
163166

164-
bool found = span.Contains(target);
165-
Assert.False(found);
167+
Assert.False(span.Contains(target));
166168
}
167169
}
168170

@@ -181,8 +183,7 @@ public static void TestMultipleMatchContains_String()
181183
a[length - 2] = "5555";
182184

183185
ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
184-
bool found = span.Contains("5555");
185-
Assert.True(found);
186+
Assert.True(span.Contains("5555"));
186187
}
187188
}
188189

@@ -192,6 +193,7 @@ public static void ContainsNull_String(string[] spanInput, bool expected)
192193
{
193194
ReadOnlySpan<string> theStrings = spanInput;
194195
Assert.Equal(expected, theStrings.Contains(null));
196+
Assert.All(GetDefaultEqualityComparers<string>(), comparer => Assert.Equal(expected, new ReadOnlySpan<string>(spanInput).Contains(null, comparer)));
195197
}
196198
}
197199
}

0 commit comments

Comments
 (0)