ValeraT1982 / ObjectsComparer

C# Framework provides mechanism to compare complex objects, allows to override comparison rules for specific properties and types.
MIT License
352 stars 86 forks source link

Dictionary comparison not working if keys are the same but values are different #25

Closed nishars closed 3 years ago

nishars commented 3 years ago

Hi, We are using this package(1.4.1) to compare 2 objects, works fine except for Dictionary objects. If the Dictionary keys are same but values are different, it says that the 2 objects are still Equal. Is there any custom comparer that can be used to compare Dictionaries?

Thanks.

ValeraT1982 commented 3 years ago

Hi @nishars. I added additional test (https://github.com/ValeraT1982/ObjectsComparer/blob/master/ObjectsComparer/ObjectsComparer.Tests/Comparer_GenericEnumerableTests.cs#L514-L528) and according to this test "If the Dictionary keys are same but values are different, it says that the 2 objects are NOT Equal".

Can you please show your code?

EnCey commented 2 years ago

Is it possible that if the Value isn't a primitive type, that the comparison doesn't work? I think I'm experiencing the same issue, I have a Dictionary<string, SomeClass> and no matter how much I change SomeClass in my second dictionary, the Comparer always reports true i.e. that both dictionaries are equal.

Update: I've isolated the problem, in my case the issue is that my value is an interface and it seems ObjectComparer only compares the interface members with each other, it doesn't check if both values are of the same derived type and if so, compares those together:

    public interface IBar { }

    public sealed class Bar : IBar
    {
      public Bar(string name) => Name = name;
      public string Name { get; }
    }

    [Fact]
    public void BarTest()
    {
      IBar b1 = new Bar("two");
      IBar b2 = new Bar("two!!!");
      var barComparer = new ObjectsComparer.Comparer<IBar>();
      var barsEqual = barComparer.Compare(b1, b2, out var barDifferences);
      Assert.False(barsEqual);
   } 

This might be by design?

For completeness, my test involving dictionaries is an extension of the above:

    public sealed class Foo
    {
      public IDictionary<string, IBar> Bars { get; init; }
    }

    [Fact]
    public void DictionaryTest()
    {
      var obj1 = new Foo { Bars = new Dictionary<string, IBar> { { "one", new Bar("one") }, { "two", new Bar("two") } } };
      var obj2 = new Foo { Bars = new Dictionary<string, IBar> { { "one", new Bar("one") }, { "two", new Bar("two!!!") } } };
      var comparer = new ObjectsComparer.Comparer<Foo>();
      var areEqual = comparer.Compare(obj1, obj2, out var differences);
      Assert.False(areEqual);
    }

Perhaps this was the author's problem as well.

ValeraT1982 commented 2 years ago

Yes. This is by design. If you create compares as new ObjectsComparer.Comparer() then only members if IBar would be compared.

EnCey commented 2 years ago

In the 2nd test I don't explicitly create a Comparer<IBar>, I do create a specific Comparer<Foo>. But that comparer also only compares IBar members, it ignores Bar.Name. That is to say, the problem is more difficult to spot in that case, and I was also unaware that inheritance hierarchies aren't supported out of the box.

ValeraT1982 commented 2 years ago

In the second test comparer comparers only members of IBar because Bars declared as IDictionary<string, IBar>