【问题标题】:How to enforce adding new public properties to the Equals method for IEquatable<T> classes如何强制将新的公共属性添加到 IEquatable<T> 类的 Equals 方法
【发布时间】:2020-03-17 13:33:22
【问题描述】:

我有包含其他子类的类,因此我实现了IEquatable&lt;T&gt; 以递归地执行自定义Equals 方法。这很好,但我在想如果其他开发人员需要向这些类添加新的公共属性,我们希望强制他们也将它们添加到 Equals 方法中。我想知道是否有一种简单的方法可以在没有反射的情况下做到这一点?我可以以某种方式利用自定义属性吗?我基本上想将它添加到单元测试套件中,这样构建就会失败。

【问题讨论】:

  • 少写你自己的分析器(这充其量是不确定的),不。我的建议是在你的代码中放一个大的(完全离谱的)注释,并编写单元测试
  • 使用静态构造函数生成一个等于Func&lt;T,bool&gt;的方法?
  • @MichaelRandall 是的,我考虑过这种性质的评论,但它并没有真正强制执行任何操作。另外,我的团队通常会尽可能避免使用 cmets。但是感谢您的回复!
  • @JeremyLakeman -- 嗯,这是一个有趣的答案。我还不知道那会是什么样子,但它为调查开辟了一条途径——谢谢!如果你有时间,你能把这个想法再扩展一点吗?
  • @MichaelRandall 哈哈——是的,我完全理解!并不是我们不写 cmets(我们的公共方法或更复杂的逻辑上肯定有 xml 文档),更多的是它们仅在适当的命名和清晰的代码仍然无法传达信息的情况下使用。但在这种情况下,这可能是要走的路。 :)

标签: c# unit-testing asp.net-core iequatable


【解决方案1】:

如果你想强制你的相等方法以标准方式构建,也许你可以在运行时使用反射编译它们;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-11
    • 2016-04-24
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多