通用 IEqualityComparer 使用反射和属性来标记我想要比较的内容

Posted

技术标签:

【中文标题】通用 IEqualityComparer 使用反射和属性来标记我想要比较的内容【英文标题】:generic IEqualityComparer using reflection and attributes to mark what i want to compare 【发布时间】:2015-02-03 08:34:52 【问题描述】:

Intersect 应该产生 0 条记录,因为 firstname 和 lastname 不匹配。 我哪里做错了....?我怀疑是在 equals 实现中。

谢谢你所有的问题都解决了(特别感谢 Lasse V. Karlsen 用 gethashcode 赚钱):Equals 加上哈希码错误解决了这个问题。

测试代码

    var test1 = new ClassA  employeeid = 1, firstName = "a", lastname = "a" ;
    var test2 = new ClassA  employeeid = 1, firstName = "a", lastname = "b" ;
    IList<ClassA> listA = new List<ClassA>();
    listA.Add(test1);

    IList<ClassA> listB = new List<ClassA>();

    listB.Add(test2);

    //Actual Code
    var Reflection = new ReflectionHelper();
    var ListClassA = Reflection.GetPropertyNames<ClassA>();
    var results = listA.Intersect(listB, new Compare<ClassA>(ListClassA)).ToList();

我的比较器

 public class KeyComparerAttribute : Attribute
 public class Compare<T> : IEqualityComparer<T> where T : class
        

            IList<string> keyProperties = new List<string>(); 
            public Compare(IList<string> keyProperties)
            
                this.keyProperties = keyProperties; 
            


            public  bool Equals(T x, T y)
            
                if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                
                    return false;
                

                var reflection = new ReflectionHelper();
                foreach (var propName in keyProperties)
                
                    string val1 = reflection.GetPropertyValue<T>(propName, x);
                    string val2 = reflection.GetPropertyValue<T>(propName, y);
                    if (!val1.Equals(val2))
                    
                      return false; 
                    
                
                //if never false then it must be true....
                return true;
                    
            public  int GetHashCode(T obj)
            
                int hash = 17;
                foreach (var propInfo in keyProperties)
                
                    var myValue = reflection.GetPropertyValue(propName:propInfo, src: obj);
                     hash = hash * 23 + myValue.GetHashCode();
                
                return hash;
            


         

我的助手类仅供参考

public class ReflectionHelper 
        
            public IList<string> GetPropertyNames<T>() 
            
                IList<string> propertyNames = new List<string>(); 
                var propertyInfos = typeof(T).GetProperties(
                    BindingFlags.Public |
                    BindingFlags.Static |
                    BindingFlags.NonPublic |
                    BindingFlags.Default |
                    BindingFlags.Instance).ToList().
                    Where(prop => Attribute.IsDefined(prop, typeof(KeyComparerAttribute)));
                // write property names
                if (propertyInfos.ToList().Count > 0)
                
                    foreach (PropertyInfo propertyInfo in propertyInfos)
                    
                       propertyNames.Add(propertyInfo.Name);
                    
                
                return propertyNames;
            
            public string GetPropertyValue<T>(string propName, T src)
            
                return src.GetType().GetProperty(propName).GetValue(src, null).ToString();
            
    

A 类

 public class ClassA
    

        public int employeeid  get; set; 
        [KeyComparer]
        public string firstName  get; set; 
        [KeyComparer]
        public string lastname  get; set; 
    

【问题讨论】:

无论如何,即使比较器确实有效,他也会将相同的两个实例放在两个列表中,我不明白他在这里测试什么。 此外,他为他的场景实现了一个非常不正确的 GetHashCode,它应该使用与比较器相同的属性来计算哈希码。 它们实际上应该是相同的哈希码属性。 我是说你的实现是错误的。您阅读了 propertyinfo 对象,它们总是相同的。它应该基于这些属性的值。 请不要更新您的问题以包含答案。如果您回答了自己的问题,请将您的解决方案作为答案发布并接受。 ***.com/help/self-answer 【参考方案1】:

根据我的高中数学:

SetA = test1, test2
SetB = test1, test2
SetA = SetB (conceptually speaking) => SetA (intersect) SetB = SetA

或者你期望看到什么?

如果你改成

SetA = test1
SetB = test2

你会得到0(空集)。

无论如何,我不建议将此作为产品代码。至少,当您已经在 ReflectionHelper 中检查了一个类时,缓存属性信息。

如果您有很多比较(正如您在实现此代码的推理中所暗示的那样),此代码将运行(比较)非常缓慢。虽然我非常喜欢避免编写样板代码,但由于性能/可维护性的原因,有时值得编写它。

【讨论】:

这就是我得到结果 =2 的重点。我的目标是按照你的高中数学要求得到 0。 使用 except 而不是 intersect then ;-) var results = listA.Except(listB, new Compare(ListClassA)).ToList(); 大概是 LasseV.Karlsen 说的 ==。我需要将对 == 的任何调用替换为等于...我认为... 问题是相交背后的数学。如果将 == 替换为 .equals,则不会有任何区别。对于您的情况,由于实习, string.equals 与 == 相同,因此您很幸运(尽管您应该将其替换为 .equals 以确保它也适用于复杂类型)。 相交意味着给定一个子集 a1 2 3 intersect b1 结果集是 c1 或者我疯了。另外我已经做了具体的实现并且这个工作但我失败了在通用的。

以上是关于通用 IEqualityComparer 使用反射和属性来标记我想要比较的内容的主要内容,如果未能解决你的问题,请参考以下文章

为Distinct准备的通用对比器

使用 IEqualityComparer 进行联合

List Except 操作,IEqualityComparer 使用

在 IEqualityComparer 中包装委托

使用反射与数据库链接的通用列表

如何使用反射动态创建通用 C# 对象? [复制]