|
// 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>));
}
}
}
|