// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.Serialization; namespace System.Collections.Generic { [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public abstract partial class Comparer<T> : IComparer, IComparer<T> { // public static Comparer<T> Default is runtime-specific public static Comparer<T> Create(Comparison<T> comparison) { ArgumentNullException.ThrowIfNull(comparison); return new ComparisonComparer<T>(comparison); } public abstract int Compare(T? x, T? y); int IComparer.Compare(object? x, object? y) { if (x == null) return y == null ? 0 : -1; if (y == null) return 1; if (x is T && y is T) return Compare((T)x, (T)y); ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); return 0; } } internal sealed class ComparisonComparer<T> : Comparer<T> { private readonly Comparison<T> _comparison; public ComparisonComparer(Comparison<T> comparison) { _comparison = comparison; } public override int Compare(T? x, T? y) => _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) // means another generic instantiation, which can be costly esp. // for value types. [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] // Needs to be public to support binary serialization compatibility public sealed partial class GenericComparer<T> : Comparer<T> where T : IComparable<T>? { [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int Compare(T? x, T? y) { if (x != null) { if (y != null) return x.CompareTo(y); return 1; } if (y != null) return -1; return 0; } // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) => obj != null && GetType() == obj.GetType(); public override int GetHashCode() => GetType().GetHashCode(); } [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] // Needs to be public to support binary serialization compatibility public sealed class NullableComparer<T> : Comparer<T?>, ISerializable where T : struct { public NullableComparer() { } private NullableComparer(SerializationInfo info, StreamingContext context) { } public void GetObjectData(SerializationInfo info, StreamingContext context) { if (!typeof(T).IsAssignableTo(typeof(IComparable<T>))) { // We used to use NullableComparer only for types implementing IComparable<T> info.SetType(typeof(ObjectComparer<T?>)); } } public override int Compare(T? x, T? y) { if (x.HasValue) { if (y.HasValue) return Comparer<T>.Default.Compare(x.value, y.value); return 1; } if (y.HasValue) return -1; return 0; } // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) => obj != null && GetType() == obj.GetType(); public override int GetHashCode() => GetType().GetHashCode(); } [Serializable] [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] // Needs to be public to support binary serialization compatibility public sealed partial class ObjectComparer<T> : Comparer<T> { public override int Compare(T? x, T? y) { return Comparer.Default.Compare(x, y); } // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) => obj != null && GetType() == obj.GetType(); public override int GetHashCode() => GetType().GetHashCode(); } [Serializable] internal sealed partial class EnumComparer<T> : Comparer<T>, ISerializable where T : struct, Enum { public EnumComparer() { } // Used by the serialization engine. private EnumComparer(SerializationInfo info, StreamingContext context) { } // public override int Compare(T x, T y) is runtime-specific // Equals method for the comparer itself. public override bool Equals([NotNullWhen(true)] object? obj) => obj != null && GetType() == obj.GetType(); public override int GetHashCode() => GetType().GetHashCode(); public void GetObjectData(SerializationInfo info, StreamingContext context) { // Previously Comparer<T> was not specialized for enums, // and instead fell back to ObjectComparer which uses boxing. // Set the type as ObjectComparer here so code that serializes // Comparer for enums will not break. info.SetType(typeof(ObjectComparer<T>)); } } } |