Как обеспечить добавление новых свойств publi c в метод Equals для классов IEquatable <T> - PullRequest
0 голосов
/ 17 марта 2020

У меня есть классы, которые содержат другие дочерние классы, поэтому я реализовал IEquatable<T> для рекурсивного выполнения пользовательского метода Equals по цепочке. Это работает нормально, но я думаю, что если другим разработчикам нужно добавить новые свойства publi c в эти классы, мы бы хотели, чтобы они также добавляли их в метод Equals. Мне было интересно, есть ли простой способ сделать это без размышлений? Могу ли я как-то использовать пользовательские атрибуты? Я бы хотел добавить его в комплект модульных тестов, чтобы сборка не удалась.

1 Ответ

1 голос
/ 17 марта 2020

Если вы хотите, чтобы ваши методы равенства были построены стандартным образом, возможно, вы могли бы скомпилировать их во время выполнения с помощью отражения;

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

...