未验证 提交 24f1acda 编写于 作者: S Stephen Toub 提交者: GitHub

Special-case Enumerable.SequenceEqual for byte[] (#48287)

上级 970d347a
......@@ -12,11 +12,6 @@ public static partial class Enumerable
public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
{
if (comparer == null)
{
comparer = EqualityComparer<TSource>.Default;
}
if (first == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
......@@ -27,6 +22,24 @@ public static bool SequenceEqual<TSource>(this IEnumerable<TSource> first, IEnum
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
if (comparer == null)
{
// It's relatively common to see code (especially in tests and testing frameworks) that ends up
// using Enumerable.SequenceEqual to compare two byte arrays. Using ReadOnlySpan.SequenceEqual
// is significantly faster than accessing each byte via the array's IList<byte> interface
// implementation. So, we special-case byte[] here. It would be nice to be able to delegate
// to ReadOnlySpan.SequenceEqual for all TSource[] arrays where TSource is a value type and
// implements IEquatable<TSource>, but there's no good way without reflection to convince
// the C# compiler to let us delegate, as ReadOnlySpan.SequenceEqual requires an IEquatable<T>
// constraint on its type parameter, and Enumerable.SequenceEqual lacks one on its type parameter.
if (typeof(TSource) == typeof(byte) && first is byte[] firstArr && second is byte[] secondArr)
{
return ((ReadOnlySpan<byte>)firstArr).SequenceEqual(secondArr);
}
comparer = EqualityComparer<TSource>.Default;
}
if (first is ICollection<TSource> firstCol && second is ICollection<TSource> secondCol)
{
if (firstCol.Count != secondCol.Count)
......
......@@ -214,5 +214,37 @@ public void SecondSourceNull()
AssertExtensions.Throws<ArgumentNullException>("second", () => first.SequenceEqual(second));
}
[Fact]
public void ByteArrays_SpecialCasedButExpectedBehavior()
{
AssertExtensions.Throws<ArgumentNullException>("first", () => ((byte[])null).SequenceEqual(new byte[1]));
AssertExtensions.Throws<ArgumentNullException>("second", () => new byte[1].SequenceEqual(null));
Assert.False(new byte[1].SequenceEqual(new byte[0]));
Assert.False(new byte[0].SequenceEqual(new byte[1]));
var r = new Random();
for (int i = 0; i < 32; i++)
{
byte[] arr = new byte[i];
r.NextBytes(arr);
byte[] same = (byte[])arr.Clone();
Assert.True(arr.SequenceEqual(same));
Assert.True(same.SequenceEqual(arr));
Assert.True(same.SequenceEqual(arr.ToList()));
Assert.True(same.ToList().SequenceEqual(arr));
Assert.True(same.ToList().SequenceEqual(arr.ToList()));
if (i > 0)
{
byte[] diff = (byte[])arr.Clone();
diff[^1]++;
Assert.False(arr.SequenceEqual(diff));
Assert.False(diff.SequenceEqual(arr));
}
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册