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); private static class TypeReflectionCache { public static readonly Func IsEqualFunc; 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); } } #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); #pragma warning restore CS8604 // Possible null reference argument. } }