Если вы хотите, чтобы ваши методы равенства были построены стандартным образом, возможно, вы могли бы скомпилировать их во время выполнения с помощью отражения;
private static Expression Equality(Type propType, MemberExpression thisProp, MemberExpression otherProp)
{
var equatable = typeof(IEquatable<>).MakeGenericType(propType);
var equal = Expression.Equal(thisProp, otherProp);
if (!equatable.IsAssignableFrom(propType))
return equal;
// a == b || (a!=null && a.Equals(b))
return Expression.OrElse(
equal,
Expression.AndAlso(
Expression.NotEqual(thisProp, Expression.Constant(null, propType)),
Expression.Call(thisProp, equatable.GetMethod("Equals"), otherProp)
)
);
}
private static Delegate GenerateEquatable(Type type)
{
var thisParm = Expression.Parameter(type, "a");
var otherParm = Expression.Parameter(type, "b");
return Expression.Lambda(
type.GetProperties()
.Where(prop => prop.CanRead)
.Select(prop => Equality(
prop.PropertyType,
Expression.MakeMemberAccess(thisParm, prop),
Expression.MakeMemberAccess(otherParm, prop)))
.Aggregate((a, b) => Expression.AndAlso(a, b)),
thisParm, otherParm).Compile();
}
public static Func<T, T, bool> GenerateEquatable<T>() where T:IEquatable<T> =>
(Func<T, T, bool>)GenerateEquatable(typeof(T));
public class Foo : IEquatable<Foo>{
private static Func<Foo, Foo, bool> _equals = GenerateEquatable<Foo>();
public bool Equals([AllowNull] Foo other) => other != null && _equals(this, other);
}