using System.Reflection; namespace Just.Railway; internal static class ReflectionHelper { [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEqual(T? left, T? right) => TypeReflectionCache.IsEqualFunc(left, right); [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Compare(T? left, T? right) => TypeReflectionCache.CompareFunc(left, right); private static class TypeReflectionCache { public static readonly Func IsEqualFunc; public static readonly Func CompareFunc; static TypeReflectionCache() { var type = typeof(T); var isNullableStruct = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); var underlyingType = isNullableStruct ? type.GenericTypeArguments.First() : type; var thisType = typeof(TypeReflectionCache); var equatableType = typeof(IEquatable<>).MakeGenericType(underlyingType); if (equatableType.IsAssignableFrom(underlyingType)) { var isEqualFunc = thisType.GetMethod(isNullableStruct ? nameof(IsEqualNullable) : nameof(IsEqual), BindingFlags.Static | BindingFlags.Public) !.MakeGenericMethod(underlyingType); IsEqualFunc = (Func)Delegate.CreateDelegate(typeof(Func), isEqualFunc); } else { IsEqualFunc = static (left, right) => left is null ? right is null : left.Equals(right); } var comparableType = typeof(IComparable<>).MakeGenericType(underlyingType); if (comparableType.IsAssignableFrom(underlyingType)) { var compareFunc = thisType.GetMethod(isNullableStruct ? nameof(CompareNullable) : nameof(Compare), BindingFlags.Static | BindingFlags.Public) !.MakeGenericMethod(underlyingType); CompareFunc = (Func)Delegate.CreateDelegate(typeof(Func), compareFunc); } else { CompareFunc = static (left, right) => left is null ? right is null ? 0 : -1 : right is null ? 1 : left.GetHashCode().CompareTo(right.GetHashCode()); } } #pragma warning disable CS8604 // Possible null reference argument. [Pure] public static bool IsEqual(R? left, R? right) where R : notnull, IEquatable, T => left is null ? right is null : left.Equals(right); [Pure] public static bool IsEqualNullable(R? left, R? right) where R : struct, IEquatable => left is null ? right is null : right is not null && left.Value.Equals(right.Value); [Pure] public static int Compare(R? left, R? right) where R : notnull, IComparable, T => left is null ? right is null ? 0 : -1 : right is null ? 1 : left.CompareTo(right); [Pure] public static int CompareNullable(R? left, R? right) where R : struct, IComparable => left is null ? right is null ? 0 : -1 : right is null ? 1 : left.Value.CompareTo(right.Value); #pragma warning restore CS8604 // Possible null reference argument. } }