0

The Intersect should have yielded 0 records because firstname and lastname do not macth. Where did I go wrong....? I suspect is in the equals implementation.

Thankyou all the problem is solved ( special thanks to Lasse V. Karlsen right on the money with the gethashcode ): Equals plus the hashcode mistake fixed the problem.

Test Code

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

My comparer

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


        } 

My helper classes just for reference

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

Class A

 public class ClassA
    {

        public int employeeid { get; set; }
        [KeyComparer]
        public string firstName { get; set; }
        [KeyComparer]
        public string lastname { get; set; }
    }
hidden
  • 3,216
  • 8
  • 47
  • 69
  • In any case, even if the comparer did work, he would be putting the same two instances in both lists, I don't understand what he's testing here. – Lasse V. Karlsen Dec 05 '14 at 08:11
  • Additionally he has implemented a very incorrect GetHashCode for his scenario, it should use the same properties as the comparer does to calculate the hashcode. – Lasse V. Karlsen Dec 05 '14 at 08:12
  • They should be the same properties for the hashcode actually. – hidden Dec 05 '14 at 08:13
  • The list lastname property value is b in one list and the other is a. Hence, intersect should return 0. – hidden Dec 05 '14 at 08:14
  • @hidden: You're currently calling `GetHashCode()` on the `PropertyInfo` for the properties, not the *values* of the properties... – Jon Skeet Dec 05 '14 at 08:16
  • According to: http://msdn.microsoft.com/en-us/library/ms132151%28v=vs.110%29.aspx and I am doing the same thing but with reflection. if (b1.Height == b2.Height & b1.Length == b2.Length & b1.Width == b2.Width) { return true; } – hidden Dec 05 '14 at 08:18
  • The implementation from gethashcode is straight from http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode u r saying that is wrong? Should I have gotten the value on the line typeof(T).GetProperty(propInfo) – hidden Dec 05 '14 at 08:21
  • 1
    I'm saying your implementation is wrong. You read the propertyinfo objects, which will always be the same. It should be based on the values of those properties. – Lasse V. Karlsen Dec 05 '14 at 08:24
  • K i see that correcting that now. – hidden Dec 05 '14 at 08:24
  • @hidden Can you please paste your updated code? I'm very curious to see how it 'fixes' the problem. I'll even run it in VS just to make sure :). Unless there's a bug in the .NET framework, the behavior of returning 2 items as a result of your intersect, given your input, is the correct one. edit: nevermind, you changed it so that listA is only test1 and listB is test2. That would indeed yield 0 as intersection but it's different to the original problem :). – kha Dec 05 '14 at 09:07
  • Yes i updated the question to reflect the final solution. – hidden Dec 06 '14 at 21:39
  • 1
    Please do not update your question to include answers. If you answered your own question, post your solution as an answer and accept it. http://stackoverflow.com/help/self-answer – Michael Edenfield Dec 07 '14 at 00:05

1 Answers1

1

According to my high school math:

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

Or what did you expect to see?

If you change it to

SetA = {test1}
SetB = {test2}

You will get {0} (empty set).

Regardless, I wouldn't recommend this for a prod code. At the very least, cache the property infos when you've already inspected a class in your ReflectionHelper.

This code will run (comparatively) very slowly if you have lots of comparison (as you implicated in your reasoning for implementing this). Whilst I very much like avoiding writing boiler plate codes, sometimes it's worth writing it due to performance/maintainability reasons.

kha
  • 19,123
  • 9
  • 34
  • 67
  • Thats the point I get result =2. My goal is to get 0 just as your high school math dictates. – hidden Dec 05 '14 at 08:38
  • Use except instead of intersect then ;-) var results = listA.Except(listB, new Compare(ListClassA)).ToList(); – kha Dec 05 '14 at 08:39
  • Is probably LasseV.Karlsen said about ==. I need to replace any call to == to equals... i think... – hidden Dec 05 '14 at 08:39
  • the issue is the math behind intersect. If you replace == with .equals, it won't make any difference. For your case, string.equals is the same as == due to interning so you got lucky there (though you should replace it with .equals anyway to make sure it works with complex types too). – kha Dec 05 '14 at 08:43
  • Intersect means Given a subset a{1 2 3} intersect b{1} resulting set is c{1} or am i crazy.Also I already did the concrete implementation and this works but I am failing at the generic one. – hidden Dec 05 '14 at 08:43
  • Nope, you are correct. Your subset is a{1 2} and b{1 2}. Intersect of 'a' and 'b' = c {1 2} – kha Dec 05 '14 at 08:44