From d516f4753c5526ddd52de78033c25016cf81cd30 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Sun, 30 Dec 2018 08:38:06 +0100 Subject: [PATCH] Moves generic comparers to shared CoreLib (dotnet/coreclr#21649) * Moves generic comparer to shared CoreLib * Keep Index/LastIndexOf optimizations in CoreCLR only * Moved runtime-specific enum helper to runtime-specific partial type Commit migrated from https://github.com/dotnet/coreclr/commit/3b5782fa0aa4086ee72904b6052f21cba693bcf7 --- .../System.Private.CoreLib.csproj | 4 +- .../System/Collections/Generic/Comparer.CoreCLR.cs | 26 ++ .../Generic/EqualityComparer.CoreCLR.cs | 213 ++++++++++++ .../System/Collections/Generic/EqualityComparer.cs | 372 --------------------- .../src/System.Private.CoreLib.Shared.projitems | 2 + .../src/System/Collections/Generic/Comparer.cs | 55 ++- .../System/Collections/Generic/EqualityComparer.cs | 185 ++++++++++ 7 files changed, 450 insertions(+), 407 deletions(-) create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs create mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs rename src/{coreclr/src => libraries}/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs (79%) create mode 100644 src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index 6e6e453..8e98367 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -134,9 +134,9 @@ - + - + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs new file mode 100644 index 0000000..b06fe7e --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.CoreCLR.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + [TypeDependencyAttribute("System.Collections.Generic.ObjectComparer`1")] + public abstract partial class Comparer : IComparer, IComparer + { + // To minimize generic instantiation overhead of creating the comparer per type, we keep the generic portion of the code as small + // as possible and define most of the creation logic in a non-generic class. + public static Comparer Default { get; } = (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(T)); + } + + internal sealed partial class EnumComparer : Comparer where T : struct, Enum + { + public override int Compare(T x, T y) + { + return System.Runtime.CompilerServices.JitHelpers.EnumCompareTo(x, y); + } + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs new file mode 100644 index 0000000..8ef6818 --- /dev/null +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.CoreCLR.cs @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace System.Collections.Generic +{ + [TypeDependencyAttribute("System.Collections.Generic.ObjectEqualityComparer`1")] + public abstract partial class EqualityComparer : IEqualityComparer, IEqualityComparer + { + // To minimize generic instantiation overhead of creating the comparer per type, we keep the generic portion of the code as small + // as possible and define most of the creation logic in a non-generic class. + public static EqualityComparer Default { [Intrinsic] get; } = (EqualityComparer)ComparerHelpers.CreateDefaultEqualityComparer(typeof(T)); + + internal virtual int IndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) + { + if (Equals(array[i], value)) return i; + } + return -1; + } + + internal virtual int LastIndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex - count + 1; + for (int i = startIndex; i >= endIndex; i--) + { + if (Equals(array[i], value)) return i; + } + return -1; + } + } + + public sealed partial class GenericEqualityComparer : EqualityComparer where T : IEquatable + { + internal override int IndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex + count; + if (value == null) + { + for (int i = startIndex; i < endIndex; i++) + { + if (array[i] == null) return i; + } + } + else + { + for (int i = startIndex; i < endIndex; i++) + { + if (array[i] != null && array[i].Equals(value)) return i; + } + } + return -1; + } + + internal override int LastIndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex - count + 1; + if (value == null) + { + for (int i = startIndex; i >= endIndex; i--) + { + if (array[i] == null) return i; + } + } + else + { + for (int i = startIndex; i >= endIndex; i--) + { + if (array[i] != null && array[i].Equals(value)) return i; + } + } + return -1; + } + } + + public sealed partial class NullableEqualityComparer : EqualityComparer where T : struct, IEquatable + { + internal override int IndexOf(T?[] array, T? value, int startIndex, int count) + { + int endIndex = startIndex + count; + if (!value.HasValue) + { + for (int i = startIndex; i < endIndex; i++) + { + if (!array[i].HasValue) return i; + } + } + else + { + for (int i = startIndex; i < endIndex; i++) + { + if (array[i].HasValue && array[i].value.Equals(value.value)) return i; + } + } + return -1; + } + + internal override int LastIndexOf(T?[] array, T? value, int startIndex, int count) + { + int endIndex = startIndex - count + 1; + if (!value.HasValue) + { + for (int i = startIndex; i >= endIndex; i--) + { + if (!array[i].HasValue) return i; + } + } + else + { + for (int i = startIndex; i >= endIndex; i--) + { + if (array[i].HasValue && array[i].value.Equals(value.value)) return i; + } + } + return -1; + } + } + + public sealed partial class ObjectEqualityComparer : EqualityComparer + { + internal override int IndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex + count; + if (value == null) + { + for (int i = startIndex; i < endIndex; i++) + { + if (array[i] == null) return i; + } + } + else + { + for (int i = startIndex; i < endIndex; i++) + { + if (array[i] != null && array[i].Equals(value)) return i; + } + } + return -1; + } + + internal override int LastIndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex - count + 1; + if (value == null) + { + for (int i = startIndex; i >= endIndex; i--) + { + if (array[i] == null) return i; + } + } + else + { + for (int i = startIndex; i >= endIndex; i--) + { + if (array[i] != null && array[i].Equals(value)) return i; + } + } + return -1; + } + } + + public sealed partial class ByteEqualityComparer : EqualityComparer + { +#if DEBUG + internal override int IndexOf(byte[] array, byte value, int startIndex, int count) + { + Debug.Fail("Should not get here."); + return -1; + } + + internal override int LastIndexOf(byte[] array, byte value, int startIndex, int count) + { + Debug.Fail("Should not get here."); + return -1; + } +#endif + } + + public sealed partial class EnumEqualityComparer : EqualityComparer where T : struct, Enum + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T x, T y) + { + return System.Runtime.CompilerServices.JitHelpers.EnumEquals(x, y); + } + + internal override int IndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) + { + if (System.Runtime.CompilerServices.JitHelpers.EnumEquals(array[i], value)) return i; + } + return -1; + } + + internal override int LastIndexOf(T[] array, T value, int startIndex, int count) + { + int endIndex = startIndex - count + 1; + for (int i = startIndex; i >= endIndex; i--) + { + if (System.Runtime.CompilerServices.JitHelpers.EnumEquals(array[i], value)) return i; + } + return -1; + } + } +} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs deleted file mode 100644 index 82051af..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs +++ /dev/null @@ -1,372 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Security; - -using System.Globalization; -using System.Runtime; -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; -using System.Diagnostics; - -namespace System.Collections.Generic -{ - [Serializable] - [TypeDependencyAttribute("System.Collections.Generic.ObjectEqualityComparer`1")] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public abstract class EqualityComparer : IEqualityComparer, IEqualityComparer - { - // To minimize generic instantiation overhead of creating the comparer per type, we keep the generic portion of the code as small - // as possible and define most of the creation logic in a non-generic class. - public static EqualityComparer Default { [Intrinsic] get; } = (EqualityComparer)ComparerHelpers.CreateDefaultEqualityComparer(typeof(T)); - - public abstract bool Equals(T x, T y); - public abstract int GetHashCode(T obj); - - internal virtual int IndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex + count; - for (int i = startIndex; i < endIndex; i++) - { - if (Equals(array[i], value)) return i; - } - return -1; - } - - internal virtual int LastIndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex - count + 1; - for (int i = startIndex; i >= endIndex; i--) - { - if (Equals(array[i], value)) return i; - } - return -1; - } - - int IEqualityComparer.GetHashCode(object obj) - { - if (obj == null) return 0; - if (obj is T) return GetHashCode((T)obj); - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); - return 0; - } - - bool IEqualityComparer.Equals(object x, object y) - { - if (x == y) return true; - if (x == null || y == null) return false; - if ((x is T) && (y is T)) return Equals((T)x, (T)y); - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); - return false; - } - } - - // The methods in this class look identical to the inherited methods, but the calls - // to Equal bind to IEquatable.Equals(T) instead of Object.Equals(Object) - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - // Needs to be public to support binary serialization compatibility - public sealed class GenericEqualityComparer : EqualityComparer where T : IEquatable - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(T x, T y) - { - if (x != null) - { - if (y != null) return x.Equals(y); - return false; - } - if (y != null) return false; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(T obj) => obj?.GetHashCode() ?? 0; - - internal override int IndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex + count; - if (value == null) - { - for (int i = startIndex; i < endIndex; i++) - { - if (array[i] == null) return i; - } - } - else - { - for (int i = startIndex; i < endIndex; i++) - { - if (array[i] != null && array[i].Equals(value)) return i; - } - } - return -1; - } - - internal override int LastIndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex - count + 1; - if (value == null) - { - for (int i = startIndex; i >= endIndex; i--) - { - if (array[i] == null) return i; - } - } - else - { - for (int i = startIndex; i >= endIndex; i--) - { - if (array[i] != null && array[i].Equals(value)) return i; - } - } - return -1; - } - - // Equals method for the comparer itself. - // If in the future this type is made sealed, change the is check to obj != null && GetType() == obj.GetType(). - public override bool Equals(object obj) => - obj is GenericEqualityComparer; - - // If in the future this type is made sealed, change typeof(...) to GetType(). - public override int GetHashCode() => - typeof(GenericEqualityComparer).GetHashCode(); - } - - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - // Needs to be public to support binary serialization compatibility - public sealed class NullableEqualityComparer : EqualityComparer where T : struct, IEquatable - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(T? x, T? y) - { - if (x.HasValue) - { - if (y.HasValue) return x.value.Equals(y.value); - return false; - } - if (y.HasValue) return false; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(T? obj) => obj.GetHashCode(); - - internal override int IndexOf(T?[] array, T? value, int startIndex, int count) - { - int endIndex = startIndex + count; - if (!value.HasValue) - { - for (int i = startIndex; i < endIndex; i++) - { - if (!array[i].HasValue) return i; - } - } - else - { - for (int i = startIndex; i < endIndex; i++) - { - if (array[i].HasValue && array[i].value.Equals(value.value)) return i; - } - } - return -1; - } - - internal override int LastIndexOf(T?[] array, T? value, int startIndex, int count) - { - int endIndex = startIndex - count + 1; - if (!value.HasValue) - { - for (int i = startIndex; i >= endIndex; i--) - { - if (!array[i].HasValue) return i; - } - } - else - { - for (int i = startIndex; i >= endIndex; i--) - { - if (array[i].HasValue && array[i].value.Equals(value.value)) return i; - } - } - return -1; - } - - // Equals method for the comparer itself. - public override bool Equals(object obj) => - obj != null && GetType() == obj.GetType(); - - public override int GetHashCode() => - GetType().GetHashCode(); - } - - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - // Needs to be public to support binary serialization compatibility - public sealed class ObjectEqualityComparer : EqualityComparer - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(T x, T y) - { - if (x != null) - { - if (y != null) return x.Equals(y); - return false; - } - if (y != null) return false; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(T obj) => obj?.GetHashCode() ?? 0; - - internal override int IndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex + count; - if (value == null) - { - for (int i = startIndex; i < endIndex; i++) - { - if (array[i] == null) return i; - } - } - else - { - for (int i = startIndex; i < endIndex; i++) - { - if (array[i] != null && array[i].Equals(value)) return i; - } - } - return -1; - } - - internal override int LastIndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex - count + 1; - if (value == null) - { - for (int i = startIndex; i >= endIndex; i--) - { - if (array[i] == null) return i; - } - } - else - { - for (int i = startIndex; i >= endIndex; i--) - { - if (array[i] != null && array[i].Equals(value)) return i; - } - } - return -1; - } - - // Equals method for the comparer itself. - public override bool Equals(object obj) => - obj != null && GetType() == obj.GetType(); - - public override int GetHashCode() => - GetType().GetHashCode(); - } - - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - // Needs to be public to support binary serialization compatibility - public sealed class ByteEqualityComparer : EqualityComparer - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(byte x, byte y) - { - return x == y; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(byte b) - { - return b.GetHashCode(); - } - -#if DEBUG - internal override int IndexOf(byte[] array, byte value, int startIndex, int count) - { - Debug.Fail("Should not get here."); - return -1; - } - - internal override int LastIndexOf(byte[] array, byte value, int startIndex, int count) - { - Debug.Fail("Should not get here."); - return -1; - } -#endif - - // Equals method for the comparer itself. - public override bool Equals(object obj) => - obj != null && GetType() == obj.GetType(); - - public override int GetHashCode() => - GetType().GetHashCode(); - } - - [Serializable] - [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - // Needs to be public to support binary serialization compatibility - public sealed class EnumEqualityComparer : EqualityComparer, ISerializable where T : struct, Enum - { - internal EnumEqualityComparer() { } - - // This is used by the serialization engine. - private EnumEqualityComparer(SerializationInfo information, StreamingContext context) { } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - // For back-compat we need to serialize the comparers for enums with underlying types other than int as ObjectEqualityComparer - if (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))) != TypeCode.Int32) { - info.SetType(typeof(ObjectEqualityComparer)); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override bool Equals(T x, T y) - { - return System.Runtime.CompilerServices.JitHelpers.EnumEquals(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override int GetHashCode(T obj) - { - return obj.GetHashCode(); - } - - // Equals method for the comparer itself. - public override bool Equals(object obj) => - obj != null && GetType() == obj.GetType(); - - public override int GetHashCode() => - GetType().GetHashCode(); - - internal override int IndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex + count; - for (int i = startIndex; i < endIndex; i++) - { - if (System.Runtime.CompilerServices.JitHelpers.EnumEquals(array[i], value)) return i; - } - return -1; - } - - internal override int LastIndexOf(T[] array, T value, int startIndex, int count) - { - int endIndex = startIndex - count + 1; - for (int i = startIndex; i >= endIndex; i--) - { - if (System.Runtime.CompilerServices.JitHelpers.EnumEquals(array[i], value)) return i; - } - return -1; - } - } -} diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 923100d..3e52944 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -122,7 +122,9 @@ + + diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs similarity index 79% rename from src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs rename to src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs index 3bf7ebf..f60d0c5 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Comparer.cs @@ -10,13 +10,10 @@ using System.Runtime.Serialization; namespace System.Collections.Generic { [Serializable] - [TypeDependencyAttribute("System.Collections.Generic.ObjectComparer`1")] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public abstract class Comparer : IComparer, IComparer + public abstract partial class Comparer : IComparer, IComparer { - // To minimize generic instantiation overhead of creating the comparer per type, we keep the generic portion of the code as small - // as possible and define most of the creation logic in a non-generic class. - public static Comparer Default { get; } = (Comparer)ComparerHelpers.CreateDefaultComparer(typeof(T)); + // public static Comparer Default is runtime-specific public static Comparer Create(Comparison comparison) { @@ -38,6 +35,21 @@ namespace System.Collections.Generic } } + internal sealed class ComparisonComparer : Comparer + { + private readonly Comparison _comparison; + + public ComparisonComparer(Comparison comparison) + { + _comparison = comparison; + } + + public override int Compare(T x, T y) + { + return _comparison(x, y); + } + } + // Note: although there is a lot of shared code in the following // comparers, we do not incorporate it into a base class for perf // reasons. Adding another base class (even one with no fields) @@ -46,7 +58,7 @@ namespace System.Collections.Generic [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] // Needs to be public to support binary serialization compatibility - public sealed class GenericComparer : Comparer where T : IComparable + public sealed partial class GenericComparer : Comparer where T : IComparable { public override int Compare(T x, T y) { @@ -70,7 +82,7 @@ namespace System.Collections.Generic [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] // Needs to be public to support binary serialization compatibility - public sealed class NullableComparer : Comparer where T : struct, IComparable + public sealed partial class NullableComparer : Comparer where T : struct, IComparable { public override int Compare(T? x, T? y) { @@ -94,7 +106,7 @@ namespace System.Collections.Generic [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] // Needs to be public to support binary serialization compatibility - public sealed class ObjectComparer : Comparer + public sealed partial class ObjectComparer : Comparer { public override int Compare(T x, T y) { @@ -109,38 +121,15 @@ namespace System.Collections.Generic GetType().GetHashCode(); } - internal sealed class ComparisonComparer : Comparer - { - private readonly Comparison _comparison; - - public ComparisonComparer(Comparison comparison) - { - _comparison = comparison; - } - - public override int Compare(T x, T y) - { - return _comparison(x, y); - } - } - - // Enum comparers (specialized to avoid boxing) - // NOTE: Each of these needs to implement ISerializable - // and have a SerializationInfo/StreamingContext ctor, - // since we want to serialize as ObjectComparer for - // back-compat reasons (see below). [Serializable] - internal sealed class EnumComparer : Comparer, ISerializable where T : struct, Enum + internal sealed partial class EnumComparer : Comparer, ISerializable where T : struct, Enum { internal EnumComparer() { } // Used by the serialization engine. private EnumComparer(SerializationInfo info, StreamingContext context) { } - public override int Compare(T x, T y) - { - return System.Runtime.CompilerServices.JitHelpers.EnumCompareTo(x, y); - } + // public override int Compare(T x, T y) is runtime-specific // Equals method for the comparer itself. public override bool Equals(object obj) => diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs new file mode 100644 index 0000000..c04f121 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs @@ -0,0 +1,185 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Diagnostics; + +namespace System.Collections.Generic +{ + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + public abstract partial class EqualityComparer : IEqualityComparer, IEqualityComparer + { + // public static EqualityComparer Default is runtime-specific + + public abstract bool Equals(T x, T y); + public abstract int GetHashCode(T obj); + + int IEqualityComparer.GetHashCode(object obj) + { + if (obj == null) return 0; + if (obj is T) return GetHashCode((T)obj); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); + return 0; + } + + bool IEqualityComparer.Equals(object x, object y) + { + if (x == y) return true; + if (x == null || y == null) return false; + if ((x is T) && (y is T)) return Equals((T)x, (T)y); + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); + return false; + } + } + + // The methods in this class look identical to the inherited methods, but the calls + // to Equal bind to IEquatable.Equals(T) instead of Object.Equals(Object) + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // Needs to be public to support binary serialization compatibility + public sealed partial class GenericEqualityComparer : EqualityComparer where T : IEquatable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T x, T y) + { + if (x != null) + { + if (y != null) return x.Equals(y); + return false; + } + if (y != null) return false; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T obj) => obj?.GetHashCode() ?? 0; + + // Equals method for the comparer itself. + // If in the future this type is made sealed, change the is check to obj != null && GetType() == obj.GetType(). + public override bool Equals(object obj) => + obj is GenericEqualityComparer; + + // If in the future this type is made sealed, change typeof(...) to GetType(). + public override int GetHashCode() => + typeof(GenericEqualityComparer).GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // Needs to be public to support binary serialization compatibility + public sealed partial class NullableEqualityComparer : EqualityComparer where T : struct, IEquatable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T? x, T? y) + { + if (x.HasValue) + { + if (y.HasValue) return x.value.Equals(y.value); + return false; + } + if (y.HasValue) return false; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T? obj) => obj.GetHashCode(); + + // Equals method for the comparer itself. + public override bool Equals(object obj) => + obj != null && GetType() == obj.GetType(); + + public override int GetHashCode() => + GetType().GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // Needs to be public to support binary serialization compatibility + public sealed partial class ObjectEqualityComparer : EqualityComparer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(T x, T y) + { + if (x != null) + { + if (y != null) return x.Equals(y); + return false; + } + if (y != null) return false; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T obj) => obj?.GetHashCode() ?? 0; + + // Equals method for the comparer itself. + public override bool Equals(object obj) => + obj != null && GetType() == obj.GetType(); + + public override int GetHashCode() => + GetType().GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // Needs to be public to support binary serialization compatibility + public sealed partial class ByteEqualityComparer : EqualityComparer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(byte x, byte y) + { + return x == y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(byte b) + { + return b.GetHashCode(); + } + + // Equals method for the comparer itself. + public override bool Equals(object obj) => + obj != null && GetType() == obj.GetType(); + + public override int GetHashCode() => + GetType().GetHashCode(); + } + + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] + // Needs to be public to support binary serialization compatibility + public sealed partial class EnumEqualityComparer : EqualityComparer, ISerializable where T : struct, Enum + { + internal EnumEqualityComparer() { } + + // This is used by the serialization engine. + private EnumEqualityComparer(SerializationInfo information, StreamingContext context) { } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + // For back-compat we need to serialize the comparers for enums with underlying types other than int as ObjectEqualityComparer + if (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(T))) != TypeCode.Int32) { + info.SetType(typeof(ObjectEqualityComparer)); + } + } + + // public override bool Equals(T x, T y) is runtime-specific + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode(T obj) + { + return obj.GetHashCode(); + } + + // Equals method for the comparer itself. + public override bool Equals(object obj) => + obj != null && GetType() == obj.GetType(); + + public override int GetHashCode() => + GetType().GetHashCode(); + } +} -- 2.7.4