EquatablesComparer
Comparator for two types related by IEquatable<T>.
using NUnit.Framework.Internal;
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace NUnit.Framework.Constraints.Comparers
{
[NullableContext(1)]
[Nullable(0)]
internal static class EquatablesComparer
{
[Nullable(0)]
private readonly struct EquatableMethodImpl
{
public MethodInfo Method { get; }
public Type Argument { get; }
public EquatableMethodImpl(MethodInfo method, Type arg)
{
Method = method;
Argument = arg;
}
}
public static EqualMethodResult Equal(object x, object y, ref Tolerance tolerance, ComparisonState state, NUnitEqualityComparer equalityComparer)
{
if (equalityComparer.CompareAsCollection && state.TopLevelComparison)
return EqualMethodResult.TypesNotSupported;
Type type = x.GetType();
Type type2 = y.GetType();
if (equalityComparer.CompareProperties && type.HasCompilerGeneratedEquals())
return EqualMethodResult.TypesNotSupported;
MethodInfo methodInfo = FirstImplementsIEquatableOfSecond(type, type2);
if ((object)methodInfo != null) {
if (tolerance.HasVariance)
return EqualMethodResult.ToleranceNotSupported;
if (!InvokeFirstIEquatableEqualsSecond(x, y, methodInfo))
return EqualMethodResult.ComparedNotEqual;
return EqualMethodResult.ComparedEqual;
}
methodInfo = FirstImplementsIEquatableOfSecond(type2, type);
if (type != type2 && (object)methodInfo != null) {
if (tolerance.HasVariance)
return EqualMethodResult.ToleranceNotSupported;
if (!InvokeFirstIEquatableEqualsSecond(y, x, methodInfo))
return EqualMethodResult.ComparedNotEqual;
return EqualMethodResult.ComparedEqual;
}
return EqualMethodResult.TypesNotSupported;
}
[return: Nullable(2)]
private static MethodInfo FirstImplementsIEquatableOfSecond(Type first, Type second)
{
EquatableMethodImpl equatableMethodImpl = default(EquatableMethodImpl);
EquatableMethodImpl[] equatableImplementations = GetEquatableImplementations(first);
for (int i = 0; i < equatableImplementations.Length; i++) {
EquatableMethodImpl equatableMethodImpl2 = equatableImplementations[i];
if (equatableMethodImpl2.Argument.IsAssignableFrom(second) && ((object)equatableMethodImpl.Argument == null || equatableMethodImpl.Argument.IsAssignableFrom(equatableMethodImpl2.Argument)))
equatableMethodImpl = equatableMethodImpl2;
}
return equatableMethodImpl.Method;
}
private static EquatableMethodImpl[] GetEquatableImplementations(Type type)
{
Type[] array = type.FindInterfaces((Type t, object _) => <GetEquatableImplementations>g__IsIEquatableOfT|2_0(t), string.Empty);
EquatableMethodImpl[] array2 = new EquatableMethodImpl[array.Length];
for (int i = 0; i < array.Length; i++) {
InterfaceMapping interfaceMap = type.GetInterfaceMap(array[i]);
MethodInfo methodInfo = interfaceMap.TargetMethods[0];
array2[i] = new EquatableMethodImpl(methodInfo, methodInfo.GetParameters()[0].ParameterType);
}
return array2;
}
private static bool InvokeFirstIEquatableEqualsSecond(object first, object second, MethodInfo equals)
{
return (bool)equals.Invoke(first, new object[1] {
second
});
}
}
}